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

