使用过AIDL进行跨进程通讯的同学,肯定遇到过DeadObjectException这个溃散,那么这个溃散是怎样来的,咱们又该怎么解决它呢?今日这篇文章就来聊一聊。
溃散来历
首要,这个溃散的意思是,多进程在进行跨进程Binder通讯的时候,发现通讯的Binder对端现已逝世了。
抛出反常的Java堆栈最后一行是BinderProxy.transactNative,所以咱们从这个办法入手,看看溃散是在哪里产生的。
很显现,transactNative对应的是一个native办法,咱们找到对应的native办法,在android_util_Binder.cpp中。
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
// 假如data数据为空,直接抛出空指针反常
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
return JNI_FALSE;
}
// 将Java层传入的目标转化为C++层的指针,假如转化犯错,中断履行,回来JNI_FALSE
Parcel* data = parcelForJavaObject(env, dataObj);
if (data == NULL) {
return JNI_FALSE;
}
Parcel* reply = parcelForJavaObject(env, replyObj);
if (reply == NULL && replyObj != NULL) {
return JNI_FALSE;
}
// 获取C++层的Binder署理目标指针
// 假如获取失利,会抛出IllegalStateException
IBinder* target = getBPNativeData(env, obj)->mObject.get();
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
// 调用BpBinder目标的transact办法
status_t err = target->transact(code, *data, reply, flags);
// 假如成功,回来JNI_TRUE,假如失利,回来JNI_FALSE
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
// 处理反常情况的抛出
signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
return JNI_FALSE;
}
能够看到,这个办法主要做的工作是:
- 将
Java层传入的data,转化成C++层的指针 - 获取
C++层的Binder署理目标 - 调用
BpBinder目标的transact办法 - 处理
transact的成果,抛出反常
接下来咱们看看,BpBinder的transact办法。
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// 首要判别Binder目标是否还存活,假如不存活,直接回来DEAD_OBJECT
if (mAlive) {
...
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
return status;
}
return DEAD_OBJECT;
}
transact的具体办法,咱们这儿先不讨论。咱们能够看到,在这儿会判别当时的Binder目标是否alive,假如不alive,会直接回来DEAD_OBJECT的状态。
回来的成果,在android_util_Binder的signalExceptionForError中处理。
void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
bool canThrowRemoteException, int parcelSize)
{
// 省掉其他反常处理的代码
....
case DEAD_OBJECT:
// DeadObjectException is a checked exception, only throw from certain methods.
jniThrowException(env, canThrowRemoteException
? "android/os/DeadObjectException"
: "java/lang/RuntimeException", NULL);
break;
}
这个办法,其实包含非常多反常情况的处理。为了看起来更清晰,这儿咱们省掉了其他反常的处理逻辑,只保留了DEAD_OBJECT的处理。能够很明显的看到,在这儿咱们抛出了DeadObjectException反常。
解决办法
经过前面的源码分析,咱们知道DeadObjectException是发生在,当咱们调用transact接口发现Binder目标不再存活的情况。
解决方案也很简单,便是当这个Binder目标逝世之后,不再调用transact接口。
办法1 调用跨进程接口之前,先判别Binder是否存活
这个方案比较简单粗犷,便是在多有调用跨进程接口的地方,都加一个Binder是否存活的判别。
if (mService != null && mService.asBinder().isBinderAlive()) {
mService.test();
}
咱们来看下isBinderAlive的源码,便是判别mAlive标志位是否为0。
bool BpBinder::isBinderAlive() const
{
return mAlive != 0;
}
办法2 监听Binder逝世通知
先初始化一个DeathRecipient,用来监听逝世通知。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 解绑当时监听,重新发动服务
mService.asBinder().unlinkToDeath(mDeathRecipient, 0);
if (mService != null)
bindService(new Intent("com.service.bind"), mService, BIND_AUTO_CREATE);
}
};
在这个逝世监听里,咱们能够选择几种处理方式:
- 什么都不做,直接将
mService设置为空 - 再次测验发动和绑定服务
在onServiceConnected办法中,注册逝世监听:
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IServiceInterface.Stub.asInterface(service);
//获取服务端供给的接口
try {
// 注册逝世署理
if(mService != null){
service.linkToDeath(mDeathRecipient, 0);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
总结
跨进程通讯时,无法防止呈现Binder对端挂掉的情况,所以在调用相关通讯接口时,一定要判别连接是否可用,否则就会呈现DeadObjectException的溃散。
