Android開發藝術探索學習筆記2——IPC機制
IPC簡介:
Windows上的IPC:
剪貼闆:所有的程序都可以設定和修改剪貼闆,也都可以從剪貼闆擷取内容
管道:實質是一種共享的記憶體,由一個程序建立,其他程序連接配接,并可進行雙向的通信。
郵槽:一個程序建立并擁有一個郵槽,其他程序都可以打開這個郵槽并向其發送消息。
Linux上的IPC:
管道:用來連接配接不同程序之間的資料流。
共享記憶體:允許兩個或多個程序共享一定的存儲區,因為不需要拷貝資料,是以這是最快的一種IPC。
信号量:信号是一種異步通信機制, 信号異步通知接收信号的程序發生了某個事件。
Android上IPC:
除了繼承自Linux的IPC之外還有Android特有的Binder。
這裡還有Socket和ContentProvider也可以算作是程序間通信。
Android中多程序
多程序的開啟:
通過android:process = “:程序名” 屬性開啟。
在安卓中每個程序都配置設定一個獨立的虛拟機,虛拟機有不同的位址空間和對象副本。
跨程序帶來的問題
是以不同的程序就擁有獨立的:1、虛拟機2、Application 3、記憶體空間。
則多程序造成的問題如下:
1. 靜态成員和單例模式完全失效(記憶體不共享)
2. 線程同步機制完全失效(記憶體不共享)
3. SharedPreference的可靠性下降(谷歌已經廢棄,不推薦)
4. Application多次建立(因為不同程序在不同的虛拟機,啟動新的虛拟機又會将目前應用跑一遍)
跨程序的解決方式
Android中的跨程序通信:1、Intent來傳遞2、共享檔案 3、SharePreference4、基于Binder的Messager 5、AIDL 5、Socket
IPC的基礎概念
Serializable接口:
這是java提供的一個序列化接口。
Serializable序列化對象會有一個serialVersionUID。在反序列化時,需要serialVersionUID和目前類相同才能夠被正常反序列化。當類結構發生改變時serialVersionUID會變更。這會導緻用原來類結構序列化的對象反序列化會不成功。是以一般選擇手動指派。
Parcelable接口:
這是Android提供的一個序列化接口。
相對于Serializable,Parcelable在Android上的序列化效率更高。
Binder:
Binder采用的是C/S結構,通過ServiceManager連接配接各種Manager(ActivityManager、WindowManager等)。
在AIDL過程中系統會生成對應的.aidl對象,裡面的Binder也是自動生成的,同時對應的方法也會在onTransact方法中生成跨程序的方法。
當用戶端和服務端在同一個程序時,方法不會走跨程序的transact過程,當跨程序時,方法需要走tracsact過程,并通過Stub(即binder)的内部代理類Proxy來完成。

在這裡AIDL僅僅是我們生成Binder的工具。
Android中的IPC機制:
使用Bundle
在bundle中放入序列化對象,之後通過Intent進行傳輸。
使用檔案共享
通過檔案存儲和共享是比較普遍和簡單的方法
同時,這裡的SharedPreference也是個典型例子。
使用Messenger
它是一個輕量級的IPC方案,底層實作是AIDL。不過整個通信過程是以串行的方式進行,不适合大批量的并發請求。
在Messenger通信過程中,處理message是在Handler中進行的。A要發送資訊給B需要持有B的Messager對象才行。
用戶端向伺服器發送資訊
服務端:
用戶端:
messager要實作通信,需要對方持有自己的messager對象。在Service中Messager對象是通過onBinder傳回給用戶端的。
伺服器向用戶端發送資訊:
伺服器改動:
用戶端的改動:
因為用戶端要處理Message是以需要建立Handler和自己的Messager,通過Message的replyTo将自己的Messager對象發送給服務端。
使用AIDL
普通的使用
服務端:
在aidl檔案夾中建立aidl的接口:
注意:aidl服務所在包名最好和用戶端的包名結構一緻。
Eg:
這是我的一個應用的aidl
用戶端的實作:
通過binderService的方式去綁定服務并擷取binder,進而實作和伺服器的通信。這裡,除了用到了aidl檔案基本上整體和程序内與service通信沒什麼差別。
用戶端對伺服器的監聽:
伺服器端修改:
在aidl檔案中建立一個.aidl的接口檔案:
然後給原來的IBookManager添加注冊監聽和取消監聽的方法
相應的在主工程下實作,将監聽放到一個管理監聽的ArrayList中
在收到新書之後去通知對應的監聽清單
用戶端修改:
在擷取服務端的binder之後向其注冊監聽
在onDestroy時解注冊監聽:
但這裡會報錯:
監聽異常的注意事項:
對象跨程序的本質;Binder會把客戶傳遞過來的序列化對象轉化并生成一個新的對象。
為了防止這個異常,Android提供了RemoteCallbackList專門用來儲存在服務端的監聽。但這個RemoteCallbackList使用方法很特殊:
AIDL的其他注意事項:
1、 無論是在服務端還是用戶端最好都運作在非UI線程中
2、 對于服務端意外死亡的情況,我們最好為服務設定死亡監聽DeathRecipient,進而重連遠端服務。
3、 AIDL權限驗證:
用戶端驗證:
a) 在要使用該該服務的Mainifest中添加permission權限
b) 在obBind方法中添加驗證方法
服務端驗證:
在服務端的onTransact中進行驗證,使用者的Uid和Pid。注意:要使用我們的自定義權限的包名必須以com.ryg開始。
使用ContentProvider
其底層也是采用Binder。看似是SQLite資料庫,實際上對于資料存儲的方式沒有要求。
主要針對的資料格式為:1、表格的形式2、檔案資料(圖檔、視訊等)
監聽:在update、insert和delete方法引起資料源改變的時候,可以通過ContentResolver的notifyChange來通知外界。
并發:query、update、insert、delete存在多線程并發,需要做好線程同步。
使用socket
不能在主線程中通路網絡。
Binder連接配接池
對于多個地方使用到aidl的情況(每個aidl通信都要通過service),為了節省資源,我們将所有aidl放到同一個Service中去處理。
用法:
在aidl檔案中申明一個IBinderPool接口
在service中實作為:
整體實作:
調用方法: