Android?AIDL通信DeadObjectException解決方法示例
崩潰來源
使用過AIDL
進行跨進程通信的同學(xué),肯定遇到過DeadObjectException
這個崩潰,那么這個崩潰是怎么來的,我們又該如何解決它呢?今天這篇文章就來聊一聊。
首先,這個崩潰的意思是,多進程在進行跨進程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; }
這個方法,其實包含非常多異常情況的處理。為了看起來更清晰,這里我們省略了其他異常的處理邏輯,只保留了DEAD_OBJECT
的處理??梢院苊黠@的看到,在這里我們拋出了DeadObjectException
異常。
解決方法
通過前面的源碼分析,我們知道DeadObjectException
是發(fā)生在,當(dāng)我們調(diào)用transact
接口發(fā)現(xiàn)Binder
對象不再存活的情況。
解決方案也很簡單,就是當(dāng)這個Binder
對象死亡之后,不再調(diào)用transact
接口。
方法1 調(diào)用跨進程接口之前,先判斷Binder是否存活
這個方案比較簡單粗暴,就是在多有調(diào)用跨進程接口的地方,都加一個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é)
跨進程通信時,無法避免出現(xiàn)Binder
對端掛掉的情況,所以在調(diào)用相關(guān)通信接口時,一定要判斷連接是否可用,否則就會出現(xiàn)DeadObjectException
的崩潰。
以上就是Android AIDL通信DeadObjectException解決方法示例的詳細內(nèi)容,更多關(guān)于Android AIDL通信的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Kotlin startActivity跳轉(zhuǎn)Activity實現(xiàn)流程詳解
在Android當(dāng)中,Activity的跳轉(zhuǎn)有兩種方法,第一個是利用startActivity(Intent intent);的方法,第二個則是利用startActivityForResult(Intent intent,int requestCode);的方法,從字面上來看,這兩者之間的差別只在于是否有返回值的區(qū)別,實際上也確實只有這兩種區(qū)別2022-12-12Android Socket實現(xiàn)多個客戶端即時通信聊天
這篇文章主要為大家詳細介紹了Android Socket實現(xiàn)多個客戶端即時通信聊天,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04android 獲取APP的唯一標(biāo)識applicationId的實例
下面小編就為大家分享一篇android 獲取APP的唯一標(biāo)識applicationId的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02Android ViewPager實現(xiàn)無限循環(huán)效果
這篇文章主要為大家詳細介紹了Android ViewPager實現(xiàn)無限循環(huán)效果的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-03-03android使用handlerthread創(chuàng)建線程示例
這篇文章主要介紹了android使用handlerthread創(chuàng)建線程,講解了這種方式的好處及為什么不使用Thread類的原因2014-01-01提升Android應(yīng)用視覺吸引效果的10個UI設(shè)計技巧
在Android應(yīng)用開發(fā)中,風(fēng)格和設(shè)計或許不是最關(guān)鍵的要素,但它們在決定Android應(yīng)用成功與否上確實扮演重要的角色,以下是10個Android應(yīng)用的UI設(shè)計技巧,還有個附加技巧,感興趣的朋友可以了解下哦2013-01-01