RemoteCallbackList
聲明
public class RemoteCallbackList<E extends IInterface>
情況
在AIDL中客戶端向服務端注冊一個回調方法時,服務端要考慮客戶端是否意外退出(客戶端因為錯誤應用Crash,或者被Kill掉了),服務端還不知道去回調客戶端,出現錯誤
客戶端和服務端進程狀態
在進程間通信過程中,很可能出現一個進程死亡的情況。如果這時活着的一方不知道另一方已經死了就會出現問題。那我們如何在A進程中獲取B進程的存活狀態呢?
android肯定給我們提供了解決方式,那就是Binder的linkToDeath和unlinkToDeath方法,linkToDeath方法需要傳入一個DeathRecipient對象,DeathRecipient類里面有個binderDied方法,當binder對象的所在進程死亡,binderDied方法就會被執行,我們就可以在binderDied方法里面做一些異常處理,釋放資源等操作了
參考:http://www.jianshu.com/p/ce3944e5ff11
Android SDK提供一個封裝好的對象:RemoteCallbackList,幫我自動處理了Link-To-Death的問題,這就幫我們剩下了很多代碼了。
...
mClientCallBack = IRemoteCallBack.Stub.asInterface(callback);
if (mClientDeathHandler == null) {
mClientDeathHandler = new ClientDeathRecipient();
}
mClientCallBack.asBinder().linkToDeath(new ClientDeathRecipient(), 0);
...
private class ClientDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
mCallbackList.unregister(mClientCallBack);
mClientCallBack = null;
Logger.d(TAG,"client is died");
}
}
上面是在server端對client的回調接口的binder對象設置的DeathRecipient。在client死亡時,解注冊client的回調,並且置空。
有注冊回調就肯定有解注冊,但是client端與server不在一個進程,server是無法得知client解注冊時傳入的回調接口是哪一個(client調用解注冊時,是通過binder傳輸到server端,所以解注冊時的回調接口是新創建的,而不是注冊時的回調接口)。為了解決這個問題,android提供了RemoteCallbackList這個類來專門管理remote回調的注冊與解注冊。具體參考RemoteCallbackList的源碼
Messenger與AIDL的異同
其實Messenger的底層也是用AIDL實現的,但用起來還是有些不同的,這里總結了幾點區別:
1. Messenger本質也是AIDL,只是進行了封裝,開發的時候不用再寫.aidl文件。
結合我自身的使用,因為不用去寫.aidl文件,相比起來,Messenger使用起來十分簡單。但前面也說了,Messenger本質上也是AIDL,故在底層進程間通信這一塊,兩者的效率應該是一樣的。
2. 在service端,Messenger處理client端的請求是單線程的,而AIDL是多線程的。
使用AIDL的時候,service端每收到一個client端的請求時,就在BInder線程池中取一個線程去執行相應的操作。而Messenger,service收到的請求是放在Handler的MessageQueue里面,Handler大家都用過,它需要綁定一個Thread,然后不斷poll message執行相關操作,這個過程是同步執行的。
3. client的方法,使用AIDL獲取返回值是同步的,而Messenger是異步的。
Messenger只提供了一個方法進行進程間通信,就是send(Message msg)方法,發送的是一個Message,沒有返回值,要拿到返回值,需要把client的Messenger作為msg.replyTo參數傳遞過去,service端處理完之后,在調用客戶端的Messenger的send(Message msg)方法把返回值傳遞回client,這個過程是異步的,而AIDL你可以自己指定方法,指定返回值,它獲取返回值是同步的(如果沒有用oneway修飾方法的話)。
總的來說,AIDL靈活性更高,如果需要IPC通信的地方比較多,還是更推薦自定義AIDL一點。
aidl
aidl中有onway,in,out,inout關鍵字修飾
oneway
正常情況下Client調用AIDL接口方法時會阻塞,直到Server進程中該方法被執行完。oneway可以修飾AIDL文件里的方法,oneway修飾的方法在用戶請求相應功能時不需要等待響應可直接調用返回,非阻塞效果,該關鍵字可以用來聲明接口或者聲明方法,如果接口聲明中用到了oneway關鍵字,則該接口聲明的所有方法都采用oneway方式。(注意,如果client和Server在同一進程中,oneway修飾的方法還是會阻塞),oneway只能針對某一個接口,不能直接定義方法。 方法必須是void類型的返回值類型
in
非基本數據類型和string的參數類型必須加參數修飾符,in的意思是只輸入,既最終server端執行完后不會影響到參數對象
out
與in相反,out修飾的參數只能由server寫入並傳遞到client,而client傳入的值並不會傳遞到server,使用out修飾,如果參數是自定義了類型,必須實現parcelable接口,並且實現public void readFromParcel(Parcel in)方法
inout
被inout修飾的參數,既可以從client傳遞到server,也可以server傳遞到client,使用out修飾,如果參數是自定義了類型,必須實現Parcelable接口,並且實現public void readFromParcel(Parcel in)方法
如果aidl中使用了自定義類型,必須實現Parcelable接口,並新建一個和自定義類型名相同的aidl,內容就是parcelable 接口名稱