Android?AIDL通信DeadObjectException解決方法示例
崩潰來(lái)源
使用過(guò)AIDL進(jìn)行跨進(jìn)程通信的同學(xué),肯定遇到過(guò)DeadObjectException這個(gè)崩潰,那么這個(gè)崩潰是怎么來(lái)的,我們又該如何解決它呢?今天這篇文章就來(lái)聊一聊。
首先,這個(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é)果,拋出異常
接下來(lái)我們看看,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í)包含非常多異常情況的處理。為了看起來(lái)更清晰,這里我們省略了其他異常的處理邏輯,只保留了DEAD_OBJECT的處理??梢院苊黠@的看到,在這里我們拋出了DeadObjectException異常。
解決方法
通過(guò)前面的源碼分析,我們知道DeadObjectException是發(fā)生在,當(dāng)我們調(diào)用transact接口發(fā)現(xiàn)Binder對(duì)象不再存活的情況。
解決方案也很簡(jiǎn)單,就是當(dāng)這個(gè)Binder對(duì)象死亡之后,不再調(diào)用transact接口。
方法1 調(diào)用跨進(jìn)程接口之前,先判斷Binder是否存活
這個(gè)方案比較簡(jiǎn)單粗暴,就是在多有調(diào)用跨進(jìn)程接口的地方,都加一個(gè)Binder是否存活的判斷。
if (mService != null && mService.asBinder().isBinderAlive()) {
mService.test();
}
我們來(lái)看下isBinderAlive的源碼,就是判斷mAlive標(biāo)志位是否為0。
bool BpBinder::isBinderAlive() const
{
return mAlive != 0;
}
方法2 監(jiān)聽(tīng)Binder死亡通知
先初始化一個(gè)DeathRecipient,用來(lái)監(jiān)聽(tīng)死亡通知。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 解綁當(dāng)前監(jiān)聽(tīng),重新啟動(dòng)服務(wù)
mService.asBinder().unlinkToDeath(mDeathRecipient, 0);
if (mService != null)
bindService(new Intent("com.service.bind"), mService, BIND_AUTO_CREATE);
}
};
在這個(gè)死亡監(jiān)聽(tīng)里,我們可以選擇幾種處理方式:
- 什么都不做,直接將
mService設(shè)置為空 - 再次嘗試啟動(dòng)和綁定服務(wù)
在onServiceConnected方法中,注冊(cè)死亡監(jiān)聽(tīng):
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í),無(wú)法避免出現(xiàn)Binder對(duì)端掛掉的情況,所以在調(diào)用相關(guān)通信接口時(shí),一定要判斷連接是否可用,否則就會(huì)出現(xiàn)DeadObjectException的崩潰。
以上就是Android AIDL通信DeadObjectException解決方法示例的詳細(xì)內(nèi)容,更多關(guān)于Android AIDL通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Kotlin startActivity跳轉(zhuǎn)Activity實(shí)現(xiàn)流程詳解
在Android當(dāng)中,Activity的跳轉(zhuǎn)有兩種方法,第一個(gè)是利用startActivity(Intent intent);的方法,第二個(gè)則是利用startActivityForResult(Intent intent,int requestCode);的方法,從字面上來(lái)看,這兩者之間的差別只在于是否有返回值的區(qū)別,實(shí)際上也確實(shí)只有這兩種區(qū)別2022-12-12
Android Socket實(shí)現(xiàn)多個(gè)客戶端即時(shí)通信聊天
這篇文章主要為大家詳細(xì)介紹了Android Socket實(shí)現(xiàn)多個(gè)客戶端即時(shí)通信聊天,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
android 獲取APP的唯一標(biāo)識(shí)applicationId的實(shí)例
下面小編就為大家分享一篇android 獲取APP的唯一標(biāo)識(shí)applicationId的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Android 實(shí)現(xiàn)文件夾排序功能的實(shí)例代碼
這篇文章主要介紹了Android 實(shí)現(xiàn)文件夾排序功能的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-09-09
Android ViewPager實(shí)現(xiàn)無(wú)限循環(huán)效果
這篇文章主要為大家詳細(xì)介紹了Android ViewPager實(shí)現(xiàn)無(wú)限循環(huán)效果的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03
android使用handlerthread創(chuàng)建線程示例
這篇文章主要介紹了android使用handlerthread創(chuàng)建線程,講解了這種方式的好處及為什么不使用Thread類的原因2014-01-01
android中圖片加載到內(nèi)存的實(shí)例代碼
這篇文章主要介紹了android中圖片加載到內(nèi)存的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
Android開(kāi)發(fā)之基本控件和四種布局方式詳解
這篇文章主要介紹了Android開(kāi)發(fā)之基本控件和四種布局方式詳解的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
提升Android應(yīng)用視覺(jué)吸引效果的10個(gè)UI設(shè)計(jì)技巧
在Android應(yīng)用開(kāi)發(fā)中,風(fēng)格和設(shè)計(jì)或許不是最關(guān)鍵的要素,但它們?cè)跊Q定Android應(yīng)用成功與否上確實(shí)扮演重要的角色,以下是10個(gè)Android應(yīng)用的UI設(shè)計(jì)技巧,還有個(gè)附加技巧,感興趣的朋友可以了解下哦2013-01-01

