前言
前面讲解了Service的一些基本内容。但是对于绑定服务传递数据,只局限于本地服务,无法使用服务进行跨进程间的交互。如果需要用到跨进程交互的话,需要用到一个新的技术-AIDL,这篇博客就针对AIDL如何传递内置类型数据进行讲解。对于Service不熟悉的朋友,可以先看看之前的博客:Service基础、Service高级、Service应用。
本篇博客内容如下:
先来回顾一下,Android在本地的Service中如何与其它组件进行交互的,首先Service必须实现其onBind()方法,然后在onBind方法传递一个IBinder接口的实现,而在其它组件中使用bindService()绑定一个服务,再通过其中的参数ServiceConnection对象获取到Service中定义的IBinder接口的实现。那么与Service进行数据交互,其实就是传递一个IBinder,通过这个IBinder进行交互。
而现在就碰到一个问题,在同一个进程中,是可以获取到这个Service类的,也就可以获得这个Service中定义的IBinder,但是如果在不同的应用中,即远程服务,如何获取IBinder呢?仅仅是在不同的应用定义一相同的类是没有用的,所以Android为我们提供了AIDL语言,它需要先定义一个远程调用接口,然后为该接口提供一个实现类,通过共享这个远程调用接口来达到进程间数据交互的目的,而这个接口的代码是有很多共性的,并且编写过程相当枯燥乏味,所以Android开发者为我们提供了ADIL来简化通讯接口的开发。
AIDL(Android Interface Definition Language)是Android远程调用接口的定义语言。它有它自己的一套语法规范,但是和Java类似,这并不难理解,详细的这个会后面介绍。而当你定义好一个AIDL接口之后,你会发现在gen/目录下,多出一个与定义的AIDL包名相同,文件名相同的一个Java类,这个类是编译器根据定义的AIDL接口自动生成的代码,观察之后发现其实它也是继承了Binder类(Binder是IBinder的实现类),所以它可以通过ServiceConnection进行数据传递。Service只需要暴露这个AIDL接口给客户端,让客户端也定义它,这样两个应用进程就可以通讯了。
AIDL的语法与Java接口的语法非常相似,但是存在一些差异:
- AIDL定义接口的源代码后缀必须以.aidl结尾。
- AIDL一样要指定AIDL接口的包信息package *。
- AIDL接口无需指定public、private、protected等作用域,可以理解为就是public。
- AIDL默认情况下只能传递基本类型、String、List、Map、CharSequence。
- 如果需要传递其他类型的对象,需要import对象的包名,并需要对对象进行特殊处理(之后会介绍)。
例如:
1 package com.example.aidlservicedemo.domain; 2 3 interface IDog{ 4 String getName(); 5 int getAge(); 6 }
当你声明完一个AIDL接口的时候,你会发现在项目的gen/目录下,对应包中存在一个同名的Java文件,这个文件是Android帮我们自动生成的,里面有很多代码,这里只讲一下需要注意的。查看自动生成的这个Java文件代码,会发现它定义了一个名为Stub的静态抽象类,这个Stub继承了Binder,实现了AIDL接口,当然其中也实现了AIDL接口的两个方法,粗略看一下会发现它对数据做了一个序列化和反序列化的操作。正因为AIDL对数据进行了序列化和反序列化,所以才可以在进程间传递。
定义好AIDL接口之后,就需要通过服务把接口暴露给客户端,这里Service.onBind()传递的就是这个Stub静态抽象类的实现类,其他没什么特别的。
下面通过一个Demo来演示ADIL如何传递数据的,在示例中,给出两个应用,分别实现Server与调用客户端,使用的AIDL接口就是上面给出的AIDL示例代码,这里不再重复定义。
AIDL服务:BaseTypeService.java
1 package com.example.aidlservicedemo; 2 3 import java.util.Random; 4 5 import com.example.aidlservicedemo.domain.IDog.Stub; 6 7 import android.app.Service; 8 import android.content.Intent; 9 import android.os.IBinder; 10 import android.os.RemoteException; 11 import android.util.Log; 12 13 public class BaseTypeService extends Service { 14 private final String TAG="main"; 15 private DogBinder binder=null; 16 private String[] names=new String[]{"小白","旺财","小黑"}; 17 private int[] ages=new int[]{1,2,3}; 18 19 /** 20 * Stub的实现类,Stub内部实现了Binder 21 * 内部实现AIDL定义的方法 22 */ 23 public class DogBinder extends Stub{ 24 25 @Override 26 public String getName() throws RemoteException { 27 Random random=new Random(); 28 int nextInt = random.nextInt(2); 29 return names[nextInt]; 30 } 31 32 @Override 33 public int getAge() throws RemoteException { 34 Random random=new Random(); 35 int nextInt = random.nextInt(2); 36 return ages[nextInt]; 37 } 38 } 39 40 @Override 41 public void onCreate() { 42 super.onCreate(); 43 // 实例化Binder对象 44 binder=new DogBinder(); 45 Log.i(TAG, "创建服务成功"); 46 } 47 48 @Override 49 public IBinder onBind(Intent intent) { 50 Log.i(TAG, "绑定服务成功"); 51 // 返回Binder对象 52 return binder; 53 } 54 }
客户端调用服务获取数据:
1 package com.example.aidlClientdemo; 2 3 import com.example.aidlservicedemo.domain.IDog; 4 import android.app.Activity; 5 import android.content.ComponentName; 6 import android.content.Intent; 7 import android.content.ServiceConnection; 8 import android.os.Bundle; 9 import android.os.IBinder; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 import android.widget.Toast; 14 15 public class BaseTypeActivity extends Activity { 16 private Button btn_startService, btn_endService,btn_getServiceData; 17 private IDog dogService; 18 19 @Override 20 protected void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_service); 23 24 btn_startService = (Button) findViewById(R.id.btn_startService); 25 btn_endService = (Button) findViewById(R.id.btn_endService); 26 btn_getServiceData = (Button) findViewById(R.id.btn_getServiceData); 27 28 btn_startService.setOnClickListener(click); 29 btn_endService.setOnClickListener(click); 30 btn_getServiceData.setOnClickListener(click); 31 } 32 33 private View.OnClickListener click = new OnClickListener() { 34 35 @Override 36 public void onClick(View v) { 37 switch (v.getId()) { 38 case R.id.btn_startService: 39 startService(); 40 break; 41 case R.id.btn_endService: 42 endService(); 43 break; 44 case R.id.btn_getServiceData: 45 getServiceDate(); 46 break; 47 } 48 } 49 }; 50 /* 51 * 获取数据 52 */ 53 private void getServiceDate() { 54 try { 55 if(dogService!=null){ 56 StringBuilder sBuilder=new StringBuilder(); 57 sBuilder.append("name:"+dogService.getName()); 58 sBuilder.append(" age:"+dogService.getAge()); 59 Toast.makeText(BaseTypeActivity.this, sBuilder.toString(), Toast.LENGTH_SHORT).show(); 60 } 61 else 62 { 63 Toast.makeText(BaseTypeActivity.this, "请先绑定服务", Toast.LENGTH_SHORT).show(); 64 } 65 } catch (Exception e) { 66 e.printStackTrace(); 67 } 68 } 69 70 private ServiceConnection connBase=new ServiceConnection() { 71 72 @Override 73 public void onServiceDisconnected(ComponentName name) { 74 dogService=null; 75 } 76 77 @Override 78 public void onServiceConnected(ComponentName name, IBinder service) { 79 // IDog.Stub.asInterface,获取接口 80 dogService=IDog.Stub.asInterface(service); 81 } 82 }; 83 84 /** 85 * 开始服务 86 */ 87 private void startService(){ 88 Intent intent=new Intent(); 89 intent.setAction("cn.bgxt.Service.BASE_TYPE_SERVICE"); 90 bindService(intent, connBase, BIND_AUTO_CREATE); 91 Toast.makeText(BaseTypeActivity.this, "开始绑定服务", Toast.LENGTH_SHORT).show(); 92 } 93 /** 94 * 停止服务 95 */ 96 private void endService(){ 97 if(connBase!=null) 98 { 99 unbindService(connBase); 100 // 接触绑定的时候需要回收dogService连接资源 101 // 在源码中漏了,这是后来加上的 102 dogService=null; 103 Toast.makeText(BaseTypeActivity.this, "服务解除绑定", Toast.LENGTH_SHORT).show(); 104 } 105 } 106 }
效果展示:先运行服务应用,再运行客户端应用。
总结
本篇博客只介绍了AIDL的基本结构,以及如何通过AIDL接口传递一个系统内置类型的数据。下一篇博客将介绍一下AIDL的高级应用,如何传递一个自定义对象。