1.为什么试用aidl,而不是直接通信?
2.通信过程
private android.os.IBinder mRemote;
@Override
public java.lang.String call(java.lang.String req) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(req);
// 调用了IBinder 的transact
// 第一个参数表示这一次请求执行的意图,IBinder定义了像
// INTERFACE_TRANSACTION、PING_TRANSACTION这样的几个通用命令。
// 自己使用的命令的标识值需要在FIRST_CALL_TRANSACTION和LAST_CALL_TRANSACTION之间。
// 这个参数是客户端和服务端约定好的。
// 第二个参数表示向服务端发送的数据,不能为null。
// 第三个参数表示服务端返回的数据,可以为null。
// 第四个参数flags只有0和FLAG_ONEWAY (=1)两种,默认的跨进程操作是同步的,所以transact()方法的执行会阻塞flasg=0;
//指定FLAG_ONEWAY时,表示Client的transact()是单向调用,执行后立即返回,无需等待服务端返回。
mRemote.transact(Stub.TRANSACTION_call, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
**注意:**执行 transact 后当前线程就会被阻塞,直到服务端返回结果,如果耗时太长,需要我们在子线程开启通信
再进入查看 IBinder 的 transact是空方法,查找子类Binder
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
可以看到最终调用了Binder的onTransact 方法,再查看我们的Stub抽象类,继承了Binder类,重写了onTransact方法
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_call: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.call(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
根据code不同执行不同的方法,然后将数据写入reply,最后Proxy代理类收到数据,线程继续向下执行。
最后附Binder路径: platform_frameworks_base/core/java/android/os/
2019.05.15更新,摘录 《Android 内核剖析》——柯元旦 著
Binder 是一种架构,这种架构提供了服务端接口、BInder驱动、客户端接口三个模块
从这里可以看出,客户端似乎是直接调用远程服务对应的Binder,而事实上则是通过Binder驱动进行了中转。
即 存在两个Binder对象,一个是服务端的Binder对象,另一个则是Binder驱动中的Binder对象,所不同的是Binder驱动中的对象不会再额外产生一个线程
所以,客户端发出请求——> Binder驱动——>Binder(服务端)响应
如何获取远程服务在Binder对象中对应的mRemote引用?
绑定服务需要一个 ServiceConnection 接口
public interface ServiceConnection {
void onServiceConnected(ComponentName name, IBinder service);
void onServiceDisconnected(ComponentName name);
}
该接口的 onServiceConnected 方法的第二个变量 service 当客户端请求AMS启动某个Service后,该 Service如果正常启动,那么AMS 就会在远程调用ActivityThread 类中的ApplicationThread 对象,调用的参数中会包含Service的Binder引用,然后在Application中会回调binderService 中的conn接口,因此在客户端中,可以在 onServiceConnected方法中将其册数service 保存一个全局变量,从而在客户端的任何地方都可以随时调用改远程服务这就解决了一个重要问题,即客户端如何获取远端服务的Binder引用。
一次完整的通信
Proxy 将我们的请求参数发送给ServiceManager,通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间,这时我们的客户端进入等待状态,然后 Binder 驱动向服务端的 todo 队列里面插入一条事务,
执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间(这里只是执行了拷贝命
令,并没有拷贝数据,binder 只进行一次拷贝),唤醒等待的客户端并把结果响应回来,这样就完成
了一次通讯。
参考文档
写给 Android 应用工程师的 Binder 原理剖析
Android深入浅出之Binder机制
Android进程间通信(IPC)机制Binder