使用過AIDL進(jìn)行跨進(jìn)程通信的同學(xué),肯定遇到過DeadObjectException這個(gè)崩潰,那么這個(gè)崩潰是怎么來的,我們又該如何解決它呢?今天這篇文章就來聊一聊。
崩潰來源
首先,這個(gè)崩潰的意思是,多進(jìn)程在進(jìn)行跨進(jìn)程Binder通信的時(shí)候,發(fā)現(xiàn)通信的Binder對(duì)端已經(jīng)死亡了。
拋出異常的Java堆棧最后一行是BinderProxy.transactNative,所以我們從這個(gè)方法入手,看看崩潰是在哪里產(chǎn)生的。
很顯現(xiàn),transactNative對(duì)應(yīng)的是一個(gè)native方法,我們找到對(duì)應(yīng)的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數(shù)據(jù)為空,直接拋出空指針異常
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
return JNI_FALSE;
}
// 將Java層傳入的對(duì)象轉(zhuǎn)換為C++層的指針,如果轉(zhuǎn)換出錯(cuò),中斷執(zhí)行,返回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代理對(duì)象指針
// 如果獲取失敗,會(huì)拋出IllegalStateException
IBinder* target = getBPNativeData(env, obj)->mObject.get();
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
// 調(diào)用BpBinder對(duì)象的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;
}
可以看到,這個(gè)方法主要做的事情是:
- 將
Java層傳入的data,轉(zhuǎn)換成C++層的指針 - 獲取
C++層的Binder代理對(duì)象 - 調(diào)用
BpBinder對(duì)象的transact方法 - 處理
transact的結(jié)果,拋出異常
接下來我們看看,BpBinder的transact方法。
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// 首先判斷Binder對(duì)象是否還存活,如果不存活,直接返回DEAD_OBJECT
if (mAlive) {
...
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
return status;
}
return DEAD_OBJECT;
}
transact的具體方法,我們這里先不討論。我們可以看到,在這里會(huì)判斷當(dāng)前的Binder對(duì)象是否alive,如果不alive,會(huì)直接返回DEAD_OBJECT的狀態(tài)。
返回的結(jié)果,在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;
}
這個(gè)方法,其實(shí)包含非常多異常情況的處理。為了看起來更清晰,這里我們省略了其他異常的處理邏輯,只保留了DEAD_OBJECT的處理??梢院苊黠@的看到,在這里我們拋出了DeadObjectException異常。
解決方法
通過前面的源碼分析,我們知道DeadObjectException是發(fā)生在,當(dāng)我們調(diào)用transact接口發(fā)現(xiàn)Binder對(duì)象不再存活的情況。
解決方案也很簡單,就是當(dāng)這個(gè)Binder對(duì)象死亡之后,不再調(diào)用transact接口。
方法1 調(diào)用跨進(jìn)程接口之前,先判斷Binder是否存活
這個(gè)方案比較簡單粗暴,就是在多有調(diào)用跨進(jìn)程接口的地方,都加一個(gè)Binder是否存活的判斷。
if (mService != null && mService.asBinder().isBinderAlive()) {
mService.test();
}
我們來看下isBinderAlive的源碼,就是判斷mAlive標(biāo)志位是否為0。
bool BpBinder::isBinderAlive() const
{
return mAlive != 0;
}
方法2 監(jiān)聽Binder死亡通知
先初始化一個(gè)DeathRecipient,用來監(jiān)聽死亡通知。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 解綁當(dāng)前監(jiān)聽,重新啟動(dòng)服務(wù)
mService.asBinder().unlinkToDeath(mDeathRecipient, 0);
if (mService != null)
bindService(new Intent("com.service.bind"), mService, BIND_AUTO_CREATE);
}
};
在這個(gè)死亡監(jiān)聽里,我們可以選擇幾種處理方式:
- 什么都不做,直接將
mService設(shè)置為空 - 再次嘗試啟動(dòng)和綁定服務(wù)
在onServiceConnected方法中,注冊(cè)死亡監(jiān)聽:
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IServiceInterface.Stub.asInterface(service);
//獲取服務(wù)端提供的接口
try {
// 注冊(cè)死亡代理
if(mService != null){
service.linkToDeath(mDeathRecipient, 0);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
總結(jié)
跨進(jìn)程通信時(shí),無法避免出現(xiàn)Binder對(duì)端掛掉的情況,所以在調(diào)用相關(guān)通信接口時(shí),一定要判斷連接是否可用,否則就會(huì)出現(xiàn)DeadObjectException的崩潰。