aidl 流程
为了方便理解,先将binder看做是一个黑盒子 aidl的流程图如下
demo连接添加链接描述
Androidstudio 会帮我们生成emotionAidlService,具体目录在n\build\generated\aidl_source_output_dir\debug\out\com\example\emotion\emotionAidlService.java
emotionAidlService.java 代码讲解:
其中核心代码:
stub内部类里面有两个关键函数:
1、asInterface
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.emotion.emotionAidlService))) {
return ((com.example.emotion.emotionAidlService)iin);
}
return new com.example.emotion.emotionAidlService.Stub.Proxy(obj); //核心代码1
}
该核心代码就是创建一个Proxy对象,同时将IBinder对象的obj传递给Proxy
2,onTransact()
@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;
}
case TRANSACTION_RobotLookUp:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.RobotLookUp(_arg0, _arg1); //核心代码2
reply.writeNoException();
return true;
}
case TRANSACTION_RobotWarning:
{
data.enforceInterface(descriptor);
this.RobotWarning();
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
onTransact函数功能主要是接收来自binder底层的调用,通过binder底层onTransact函数,将函数执行到核心代码2中
Prox类里面的关键函数 RobotLookUp
@Override public void RobotLookUp(int y, int p) 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.writeInt(y);
_data.writeInt(p);
boolean _status = mRemote.transact(Stub.TRANSACTION_RobotLookUp, _data, _reply, 0); //核心代码
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().RobotLookUp(y, p);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
这个函数是和aidl文件里面定义的函数名字是一样的,后面进行跨进程通信会引用到则个函数,这个的功能是创建一个parcel数据_data
,为什么必须是Parcel数据了,因为Android跨进程传递的数据必须序列化,而序列化所采用的方式就是parcel。parcel序列化,主要实现
是在native层,而binder 通信主要的部分是在nativee层。另外,如果跨进程的调用RobotLookUp有返回值,那么这个返回值将以_replay来存储和传递
emotionAidlService的类结构图:
总结
binder基于AIDL的通信流程图如下:
Client端:MainActivity;
Server端:Service;
Service的对外aidl接口如上面的案例所示,
MainActivity已经通过BindService拿到了Service的IBinder对象。
aidl通信的基本步骤如下:
- Client通过ServiceConnection获取到Server的Binder,并且封装成一个Proxy。
- 通过Proxy来同步调用IPC方法(testFunction),同时通过Parcel将参数传给Binder,最终触发Binder的
transact方法。 - Binder的transact方法最终会触发到Server上Stub的onTransact方法。
- Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将
结果写入Parcel后传回。 - 请注意:Client的Ipc方法中,执行Binder的transact时,是阻塞等待的,一直到Server逻辑执行结束后才会继
续执行。当然,如果IPC方法是oneWay的方式,那么就是非阻塞的等待。 - 当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。
Aidl是同步调用还是异步调用?
经过上述分析:aidl的的调用过程是同步调用的。
当我们需要服务端做耗时操作时,肯定是不能使用同步调用的,否则轻者影响用户体验,重者直接 ANR 或者应用崩溃。那么如何使 AIDL 的调用过程是异步的呢?
只需要把调用放到非 UI 线程即可,如果要对调用的返回做 UI 更新的话,再通过 Handler 处理即可。或者通过回调去更新UI
AIDL通信出现DeadObjectException解决方法
1、这个方案比较简单粗暴,就是在多有调用跨进程接口的地方,都加一个Binder是否存活的判断。
mEmotionAidlService.asBinder().isBinderAlive();
2、监听Binder死亡通知
先初始化一个DeathRecipient,用来监听死亡通知
IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 解绑当前监听,重新启动服务
mEmotionAidlService.asBinder().unlinkToDeath(mDeathRecipient, 0);
if (mEmotionAidlService != null){
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
}
};```
在onServiceConnected方法中,注册死亡监听:
```go
serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG,"onServiceConnected");
mEmotionAidlService = emotionAidlService.Stub.asInterface(service);
// mEmotionAidlService.asBinder().isBinderAlive();
if(mEmotionAidlService != null){
try {
mEmotionAidlService.asBinder().linkToDeath(mDeathRecipient,0);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG,"onServiceDisconnected");
mEmotionAidlService=null;
}
};