膜拜郭神
AIDL( Android Interface Definition Language)安卓接口定义语言,可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。但是,不推荐使用。
具体的使用方法参见郭神链接,这里只是说一点自己的理解~。
1. 这个东西很不好写。
很容易出错,基本上全是IBinder(接口)和Binder(实现前者)两个类型转化,而且还会自动生成一个接口类,虽然跟你定义的AIDL文件同名,但是在android studio 下的android目录下看不到,所以基本靠记,而且还有build之后才能有。
2.注意当你在远程服务里做一些UI上的事,比如弹个toast,会报错。
而且如果你看日志只看主进程的话,看不到,极其坑爹。也不会崩溃。日志如下
看起来像是你的远程服务进程没有初始化Looper,然后你在构造方法,静态方法巴拉巴拉里写了prepare,会报主线程只允许有一个Looper,所以说白了那个错误,没有在主线程更新UI。那怎么办?两种解法,一种用handler发消息,另外一种自己写个线程,然后prepare。UI操作,Loop。(主要我也不清楚这个时候的更新UI操作在哪个线程里~比较菜,可以在评论里知道下哈)
上面这个问题感觉就是服务本来就是后台的,不需要跟用户交互,所以本来应该也没这个问题~我就是试出来的,完全可以把数据返回给主进程,让主进程去展示的。
分割线就是我
又研究了一遍,还是记下来吧
AIDL 使用过程不再阐述,自行百度,下面说下跨进程发生了啥
调用过程看起来是在客户端直接调用binder然后强转调用方法即可,但是,但是,说下隐藏点。
- onserviceconnected
- 参数 IBinder 有两种类型 Binder 和 BinderProxy(没见过吧) ,其中Binder 类型是非跨进程的时候拿到的,BinderProxy 是跨进程的,然后看各自的asInterface方法(这里asinterface 需要转成你定义的接口才能调用)
onserviceConnected
IBookManager manager = IBookManager.Stub.asInterface(service)
//service 可能是Binder 类型或者是 BinderProxy,为什么是这两种类型自行百度,因为我也没看明白
Binder
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
* descriptor.
*/
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
//这个owner 在构造函数里调用attch 方法设置了,所以会返回自己
return mOwner;
}
return null;
}
BinderProxy
public IInterface queryLocalInterface(String descriptor) {
return null;
}
调用方
/**
* Cast an IBinder object into an com.test.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.test.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//这里,看到了,如果是Binder那么能查询到就会直接返回,然后调用,如果返回null(binderproxy)那么创建一个代理类回去
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.test.aidl.IBookManager))) {
return ((com.test.aidl.IBookManager) iin);
}
return new com.test.aidl.IBookManager.Stub.Proxy(obj);
}
看了上边的几段代码应该能明确了如果 是跨进程调用的话,会返回给客户端一个服务端Binder的代理对象,这个代理对象继承自stub(binder)的实现,所以具有同样的方法,至于调用参照2.
- binder 代理类的调用
客户端拿到binder (实际是个代理,客户端不知道也不care)之后,看是调用了,调用到代理的各种方法,举个栗子
@Override
public java.util.List getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.test.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
首先看到了一大堆序列化的东西,为啥需要序列化???想想intent是怎么传递数据的?为啥intent 那么传递?因为支持跨进程啊,可以看到,实际的调用是mRemote 对象,mRemote对象是啥? 上边的asInterface 里边的最后一句
return new com.test.aidl.IBookManager.Stub.Proxy(obj);
就是它了,所以mRemote 是obj 也就是一个BinderProxy 对象,敲黑板重点来了:
- 这个obj(BinderProxy 对象) 是remote 进程的binder 的代理
- 跨进程调用的实际是 transcat 方法
- Stub.TRANSACTION_getBookList 这是个id,标识了要真正调用的方法
所以过程为,客户端拿到了一个代理A,这个代理A包装了一个B 的代理B+,客户端调用 A->B+ ->驱动跨进程->B->真正的方法
remote进程 的解析,并回写结果
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
全是代理。大概是这样 client <——> Binder驱动 <——> remote,只是Binder 驱动那层我们感知不到,所以看起来是两个进程直接通信的。
另外还有messagener 通信,据说是调用优化版,没有细看代码。以上基本就是AIDL 的整个过程了,至于绑定服务相关和驱动层还没看估计短时间也不会看了。
大神链接
Binder学习指南-Weishu's Notes
就是它了