Android?AIDL通信DeadObjectException解決方法示例
崩潰來源
使用過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ì)象不再存活的情況。
解決方案也很簡(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(); }
我們來看下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
的崩潰。
以上就是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);的方法,從字面上來看,這兩者之間的差別只在于是否有返回值的區(qū)別,實(shí)際上也確實(shí)只有這兩種區(qū)別2022-12-12Android Socket實(shí)現(xiàn)多個(gè)客戶端即時(shí)通信聊天
這篇文章主要為大家詳細(xì)介紹了Android Socket實(shí)現(xiàn)多個(gè)客戶端即時(shí)通信聊天,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04android 獲取APP的唯一標(biāo)識(shí)applicationId的實(shí)例
下面小編就為大家分享一篇android 獲取APP的唯一標(biāo)識(shí)applicationId的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02Android 實(shí)現(xiàn)文件夾排序功能的實(shí)例代碼
這篇文章主要介紹了Android 實(shí)現(xiàn)文件夾排序功能的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-09-09Android ViewPager實(shí)現(xiàn)無限循環(huán)效果
這篇文章主要為大家詳細(xì)介紹了Android ViewPager實(shí)現(xiàn)無限循環(huán)效果的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03android使用handlerthread創(chuàng)建線程示例
這篇文章主要介紹了android使用handlerthread創(chuàng)建線程,講解了這種方式的好處及為什么不使用Thread類的原因2014-01-01android中圖片加載到內(nèi)存的實(shí)例代碼
這篇文章主要介紹了android中圖片加載到內(nèi)存的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09提升Android應(yīng)用視覺吸引效果的10個(gè)UI設(shè)計(jì)技巧
在Android應(yīng)用開發(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