《Android開發藝術探索》第二章IPC機制小結

1. 通過android:process開啓新的進程

進程名以“:”開頭的屬於當前應用的私有進程,其他應用的組件不可以和它跑在同一個進程中,

不以冒號開頭的爲全局進程,其他應用可以通過ShareUID方式可以和它跑在同一個進程中。要求ShareUID相同且簽名相同,可以訪問對方的私有數據,如data目錄、組建信息等。同一個應用的多進程,相當於兩個不同的應用採用了ShareUID的模式。


2. Serializable接口

配合serialVersionUID使用,自動序列化和反序列化;靜態成員和transient關鍵字成員不參加序列化過程;


3. Parcelable接口

實現Parcelable接口,即實現writeToParcel(Parcel out, int flags)和Parcel.Creator中的createFromParcel;


4. Binder

可以理解爲一種虛擬的物理設備,驅動是/dev/binder,通過ServiceManager連接AMS、WMS;

客戶端發起請求時,當前線程會被掛起直到服務器端進程返回數據,所以耗時的遠程方法不能在UI線程中調用;服務端的Binder運行在Binder的線程池中,所以不管是否耗時都應該採用同步的方式實現;


5. 手動實現一個Binder的步驟:

1)繼承IInterface接口IXXXXInterface,加入業務方法和方法id,DESCRIPTOR;

2)創建Stub類,即繼承Binder並實現IXXXXInterface接口,在Service的onBind方法中返回該對象

3)創建Proxy類,即實現IXXXXInterface,內涵IBinder類型的mRemote對象

4)linkToDeath可以在Binder死亡時收到通知,重新綁定遠程Service


6. SharePreferences

通過鍵值對存儲數據,底層採用XML,保存在/data/data/package name/shared prefs目錄下,不建議在多進程中使用。


7. Messenger

服務端創建一個Service來處理客戶端請求,同時創建一個Handler並通過它來創建一個Messenger對象,然後在Service的onBind中返回這個Messager對象底層的Binder即可。

客戶端首先綁定服務端Service,之後用服務端返回的IBinder創建一個Messenger發送Message來與服務器通信;

客戶端還可以創建一個新的Messenger並把它通過Message的replyTo參數傳遞給服務器,服務端通過replyTo可以和客戶端通信。

Message中能使用的載體只有what, arg1, arg2, Bundle以及replyTo,自定義的Parcelable對象在Message的object字段無法跨進程傳遞。

Messager是以串行方式處理客戶端發來的消息,如果大量的消息同時發送到服務端,服務端只能一個個處理;


8. AIDL

建議把所有和AIDl相關的類和文件全部放倒同一個包中,當客戶端是另一個應用時可以直接把整個包複製到客戶端工程中,使得包結構保持一致;

CopyOnWriteArrayList和ConcurrentHashMap支持併發讀寫,AtomicBoolean原子變量,

1)服務端創建Service監聽客戶端的連接請求,創建AIDL文件並實現相關方法;

2)客戶端綁定Service,通過返回的IBinder調用AIDL定義的相關方法;

3)客戶端實現一個IXXXXInterface.Stub類,生成本地Binder,然後傳到Service端,Service端回調這個客戶端Stub類的相應方法,客戶端用Handler將回調方法轉到主線程中處理

4)使用RemoteCallbackList來刪除跨進程listener的接口,RemoteCallbackList<E extends IInterface>, mCallbacks = new ArrayMap<IBinder, Callback>,當客戶端進程中止後,它能自動移除客戶端所註冊的listener,內部還實現了線程同步。用beginBroadcast和finishBroadcast來遍歷。

5)客戶端的onServiceConnected和onServiceDisconnedted方法都在UI線程中,服務端運行在Binder線程中;

6)服務端進程意外停止,可以給Binder設置DeathRecipient監聽,收到binderDied回調(Binder線程)重連遠程服務,或者在onServiceDisconnected中(UI線程)重連;


9. AIDL的權限驗證

1)在AndroidMenifest中聲明所需權限

<permission android:name="com.aaa.bbb.permission.ACCESS_BOOK_SERVICE" android:protectionLevel="normal" />

在Service的onBind方法直接返回(Stub)mBinder之前調用checkCallingOrSelfPermission("com.aaa.bbb.permission.ACCESS_BOOK_SERVICE"),

內部應用想綁定到Service中,<uses-permission android:name="com.aaa.bbb.permission.ACCESS_BOOK_SERVICE" />

2)在服務器端的onTransact方法中進行驗證,可採用權限或者Uid和Pid,獲取包名。

覆蓋onTransact,調用checkCallingOrSelfPermission驗證權限,調用getPackageManager().getPackagesForUid(getCallingUid())獲得包名;

3)通過android:permission驗證


10. ContentProvider


11. 使用Socket

1)權限聲明

<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

不能在主線程訪問網絡

2)當Service啓動時,會在線程中建立TCP服務(ServerSocket),監聽8868端口,然後等待客戶端的連接請求。客戶端連接後會生成一個新的Socket(調用ServerSocket.accept()創建),並開啓新線程處理不同的客戶端請求(BufferedReader讀,PrintWriter寫)。客戶端斷開時服務端會關閉相應的Socket(因爲客戶端斷開後,服務端的輸入流回返回null)。

3)客戶端在自線程中創建Socket連接8688端口,在while循環中讀取服務器端消息;


12.Binder連接池,將所有的AIDL放在同一個Service中處理

1)定義一個AIDL接口IBinderPool,裏面有個IBinder queryBinder(int binderCode)方法,根據不同的binderCode,返回不同的Stub的實現類的binder本地對象,這些實現類分別實現了自己的AIDL接口方法;

2)Service端創建IBinderPool的本地對象,在onBind中返回;

3)客戶端實現一個BinderPool單例類,調用bindService獲取IBinderPool,然後提供queryBinder(int binderCode),內部調用IBinderPool的queryBinder方法;

4)客戶端的Activity在自線程中調用BinderPool的queryBinder,因爲BinderPool中用了CountDownLatch,將異步操作轉成了同步操作。


發佈了20 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章