Android开发中,Binder主要用在Service中包括AIDL和Messager,当客户端bindService时服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据
AIDL全称Android接口描述语言,是Android开发工具中提供的一种文件格式,通过在文件中预先定义接口,然后快速生成Binder机制的代码,省去了手动编写Binder机制
使用AIDL来实现进程间通信分为服务端和客户端两个方面:
在客户端新建一个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();
}
}
}
}
}
通过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,下面来详细介绍下这两个类内的重要方法:
private static final java.lang.String DESCRIPTOR = "com.gavinandre.aidl.Test";
/**
* 将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);
}
}
}
/**
* 实现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();
}
}
以上6种数据类型就是AIDL所支持的所有类型,其中自定义的Parcelable对象和AIDL对象必须要显式import进来,不管是否在同一个包内,如果是Parcelable对象的话必须新建一个同名的AIDL文件,并在其中声明为Parcel类型。
AIDL中除了基本数据类型,其他类型的参数必须标上方向:in(客户端到服务端)、out(服务端到客户端)或者inout(双向),要根据实际需要指定参数类型,不能一概使用out或者inout,因为这在底层实现是有开销的
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死亡),远程调用就会失败
想要解决这个问题就要使用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是否死亡
完整使用实例:
https://blog.csdn.net/lj402159806/article/details/85095699