Android中Binder与AIDL之间的关系
Binder
- Android中特有的一种进程间通信方式,一种虚拟的物理设备,设备驱动是/dev/binder
- Android中实现了IBinder接口的一个类
- ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和相应ManagerService的桥梁
- 应用层客户端和服务端进行通信的媒介
Android开发中,Binder主要用在Service中包括AIDL和Messager,当客户端bindService时服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据
AIDL
AIDL全称Android接口描述语言,是Android开发工具中提供的一种文件格式,通过在文件中预先定义接口,然后快速生成Binder机制的代码,省去了手动编写Binder机制
AIDL通信流程
使用AIDL来实现进程间通信分为服务端和客户端两个方面:
- 服务端
服务端需要创建Service用来监听客户端请求,然后创建AIDL文件声明暴露给客户端的接口,最后在Serive中实现这个AIDL接口 - 客户端
客户端需要先绑定Service,绑定成功后将服务端返回的Binder对象转换成AIDL接口所属的类型,然后调用AIDL中的方法
通过AIDL生成Binder机制文件
在客户端新建一个AIDL文件后默认会有一个basicTypes函数(基本示例,演示可以用在AIDL中作为参数和返回值的一些基本类型,这里删除),添加一个login函数
// Test.aidl
package com.gavinandre.aidl;
interface Test {
void login(String userName, String pwd);
}
rebuild后会在build/generated/source/aidl/debug/${packageName}/目录下生成同名的java文件,下面代码就是系统生成的,只是为了方便查看稍微做了一下格式和顺序调整
package com.gavinandre.aidl;
/**
* 根据Test.aidl生成的接口
*/
public interface Test extends android.os.IInterface {
/**
* 声明login接口
*/
public void login(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException;
/**
* Stub类继承自Binder,并且实现了test接口
*/
public static abstract class Stub extends android.os.Binder implements com.gavinandre.aidl.Test {
private static final java.lang.String DESCRIPTOR = "com.gavinandre.aidl.Test";
/**
* 用于标识在transact过程中客户端所请求的到底是哪个方法
*/
static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 将Binder转换为com.gavinandre.aidl.Test接口(同进程),
* 或者包装为一个Proxy(不同进程)
*/
public static com.gavinandre.aidl.Test asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.gavinandre.aidl.Test))) {
return ((com.gavinandre.aidl.Test) iin);
}
return new com.gavinandre.aidl.Test.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
/**
* 执行login函数时提交给Binder的数据
*/
case TRANSACTION_login: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
this.login(_arg0, _arg1);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
/**
* 不同进程会创建本地代理,通过Binder与服务端的对象进行交互
*/
private static class Proxy implements com.gavinandre.aidl.Test {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* 实现login接口
*/
@Override
public void login(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(userName);
_data.writeString(pwd);
mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
}
}
Binder机制分析
通过AIDL自动生成的这个类看起来逻辑混乱,但是实际上其实很清晰,通过它可以清楚的了解到Binder的工作机制,下面代码我做了一些精简:
package com.gavinandre.aidl;
/**
* 根据Test.aidl生成的接口
*/
public interface Test extends android.os.IInterface {
/**
* 声明login接口
*/
public void login(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException;
/**
* Stub类继承自Binder,并且实现了test接口
*/
public static abstract class Stub extends android.os.Binder implements com.gavinandre.aidl.Test {
//省略...
/**
* 用于标识在transact过程中客户端所请求的到底是哪个方法
*/
static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
/**
* 将Binder转换为com.gavinandre.aidl.Test接口(同进程),
* 或者包装为一个Proxy(不同进程)
*/
public static com.gavinandre.aidl.Test asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.gavinandre.aidl.Test))) {
return ((com.gavinandre.aidl.Test) iin);
}
return new com.gavinandre.aidl.Test.Stub.Proxy(obj);
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
//省略...
switch (code) {
//省略...
/**
* 执行login函数时提交给Binder的数据
*/
case TRANSACTION_login: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
this.login(_arg0, _arg1);
reply.writeNoException();
return true;
}
//省略...
}
}
/**
* 不同进程会创建本地代理,通过Binder与服务端的对象进行交互
*/
private static class Proxy implements com.gavinandre.aidl.Test {
private android.os.IBinder mRemote;
//省略...
/**
* 实现login接口
*/
@Override
public void login(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(userName);
_data.writeString(pwd);
mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
}
}
首先声明了login方法,显然就是Test.aidl中声明的方法。同时还声明了一个整型id(TRANSACTION_login)来标识这个方法,这个id用于标识在transact过程中客户端所请求的到底是哪个方法。然后可以看到一个内部类Stub继承于Binder并且实现了Test接口,当客户端和服务端都处于同一个进程时,login方法调用不会走跨进程的transact,而当处于不同进程时才会走transact过程,这个逻辑由asInterface方法与内部代理类Proxy来完成
由此可见这个接口的核心就是它的内部类Stub和Stub的内部类Proxy,下面来详细介绍下这两个类内的重要方法:
- DESCRIPTOR
Binder的唯一标识,一般用当前Binder的类名表示private static final java.lang.String DESCRIPTOR = "com.gavinandre.aidl.Test";
- asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转换成客户端所需的AIDL接口类型对象,如果客户端和服务端位于同一进程则返回服务端Stub对象本身,如果不同进程就返回系统封装后的Stub.proxy对象/** * 将Binder转换为com.gavinandre.aidl.Test接口(同进程), * 或者包装为一个Proxy(不同进程) */ public static com.gavinandre.aidl.Test asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.gavinandre.aidl.Test))) { return ((com.gavinandre.aidl.Test) iin); } return new com.gavinandre.aidl.Test.Stub.Proxy(obj); }
- asBinder
返回当前Binder对象@Override public android.os.IBinder asBinder() { return this; }
- onTransact
运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。服务端通过Stub中定义的整型id确定请求的目标方法,接着从data中取出目标方法的所需参数(如果有参数),然后执行目标方法。当目标方法执行完毕后向reply中写入返回值(如果有返回值)。
如果onTransact方法返回false,那么客户端的请求就会失败,因此可以用这个特性来做权限验证@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } /** * 执行login函数时提交给Binder的数据 */ case TRANSACTION_login: { data.enforceInterface(descriptor); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _arg1; _arg1 = data.readString(); this.login(_arg0, _arg1); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } }
- Proxy#login
客户端发起跨进程请求才会调用此方法,内部实现:首先创建输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象(如果有的话),然后把该方法的参数信息写入_data中(如果有参数),接着调用transace方法来发起RPC(远程过程调用)请求,同时挂起当前线程,然后服务端的onTransact方法会被调用到,直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果,最后将数据放入返回值对象中返回数据/** * 实现login接口 */ @Override public void login(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(userName); _data.writeString(pwd); mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }
AIDL支持的数据类型
- 基本数据类型 int、long、char、boolean、double等,只支持定向tag in
- String和CharSequence,只支持定向tag in
- List:其中元素必须是aidl支持的数据类型,接收方得到的总是ArrayList
- Map:其中元素必须是aidl支持的数据类型,接收方得到的总是HashMap
- Parcelable:所有实现了Parcelable接口的对象
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
以上6种数据类型就是AIDL所支持的所有类型,其中自定义的Parcelable对象和AIDL对象必须要显式import进来,不管是否在同一个包内,如果是Parcelable对象的话必须新建一个同名的AIDL文件,并在其中声明为Parcel类型。
AIDL中除了基本数据类型,其他类型的参数必须标上方向:in(客户端到服务端)、out(服务端到客户端)或者inout(双向),要根据实际需要指定参数类型,不能一概使用out或者inout,因为这在底层实现是有开销的
AIDL接口中只支持方法,不支持声明静态常量
AIDL接口的管理方式
RemoteCallbackList是系统专门提供用来删除跨进程listener接口的,当客户端进程终止后,它能够自动移除客户端所注册的listener,它内部还自动实现了线程同步功能
RemoteCallbackList是一个泛型,支持管理任意的AIDL接口(所有的AIDL接口都继承自IInterface接口),如下:
public class RemoteCallbackList<E extends IInterface>
它的内部有一个Map接口专门用来保存所有的AIDL回调,这个Map的Key是IBinder类型,value是Callback类型,如下:
ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
其中CallBack封装了真正的远程listener。当客户端注册listener的时候,它会把这个listener的信息存入mCallbacks中,其中key和value分别通过下面的方式获得:
IBinder key = listener.asBinder();
Callback value = new Callback(listnener, cookie);
RemoteCallbackList的使用方式
//创建RemoteCallbackList
private RemoteCallbackList<IAidlListener> mListenerList = new RemoteCallbackList<IAidlListener>();
//添加aidl接口
mListenerList.register(listener);
//删除aidl接口
mListenerList.unregister(listener);
遍历RemoteCallbackList需要用下面的方式进行,beginBroadcast和finishBroadcast必须要配对使用,无论仅仅是获取RemoteCallbackList中元素的个数
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IAidlListener l = mListenerList.getBroadcastItem(i);
if(l != null) {
//TODO
}
}
mListenerList.finishBroadcast();
Binder死亡代理
Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,会导致客户端到服务端的Binder连接断裂(称之为Binder死亡),远程调用就会失败
想要解决这个问题就要使用DeathRecipient,DeathRecipient是系统提供用来监听Binder连接断开的一个接口,它是一个接口内部只有一个方法binderDied,当binder连接断开时就会回调binderDied,然后就可以调用unlinkToDeath移除之前绑定的binder死亡代理
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
binder.unlinkToDeath(mDeathRecipient, 0);
// TODO:这里重新绑定远程Service
}
};
在客户端绑定远程服务成功后,给binder设置死亡代理
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
service.linkToDeath(mDeathRecipient, 0);
}
}
还有用Binder的isBinderAlive也可以判断Binder是否死亡
AIDL使用实例
完整使用实例: