Android IPC機制(三) Android中的IPC方式
使用Bundle
我們知道,四大元件中的三大元件(Activity,Service,Receiver)都是支援在Intent中傳遞Bundle資料的,由於Bundle實現了Parcelable介面,所以它可以方便的在不同的程序間傳輸,基於這一點,當我們在一個程序中啟動了另一個程序的Activity,Service和Receiver,我們就可以在Bundle中附加我們需要傳輸給遠端的資訊並通過Intent傳送出去,當然,我們傳輸的資料必須能夠被序列化,比如基本型別,實現了Parcellable介面的物件,實現了Serializable介面的物件以及一些Android支援的特殊物件,具體內容可以看Bundle這個類,就可與看到所有它支援的型別,Bundle不支援的型別我們無法通過它在程序間傳遞資料。
使用檔案共享
共享檔案也是一種程序間通訊方式,兩個程序通過讀/寫同一個檔案來交換資料,比如A程序把資料寫入檔案,B程序通過讀取這個檔案來獲取資料,在Window中,一個檔案如果被加了排斥鎖將會導致其他執行緒無法對其程序訪問,包括讀和寫,而由於Android系統基於Linux,使得其併發讀寫檔案可以沒有限制的進行,甚至兩個執行緒同時對同一個檔案進行寫操作都是允許的,儘管可能出問題,通過檔案交換資料很好使用,除了可以交換一些文字資訊外,我們還可以序列化一個物件到檔案系統中的同時從另一個程序中恢復這個物件。
通過檔案共享這種方式來共享資料對檔案格式是沒有具體要求的,可以是文字檔案,也可以是XML檔案,只要讀寫雙方約定資料格式即可,通過檔案共享的方式也是有侷限性,比如併發讀寫問題,如果併發讀寫,那麼我們讀出的內容就有可能不是最新的,如果是併發寫的話就更嚴重了,因此我們要儘量避免併發寫這種情況的發生或者考慮使用執行緒同步來限制多個執行緒的寫操作,檔案共享方式適合在資料同步要求不高的程序之間進行通訊,並且要妥善處理併發讀寫
SharedPreferences是個特例,SharedPreferences是Android 中提供的輕量級儲存方案,它通過鍵值對的方式來儲存資料,在底層實現上它採用XML檔案來儲存鍵值對,每個應用的SharedPreferences檔案都可以在當前包所在的data目錄下檢視到,一般來說,它的目錄位於data/data/package name/shared_prefs目錄下,其中package name表示的是當前應用的包名,從本質來說,SharedPreferences也屬於檔案的一種,但是由於系統對它的讀寫有一定的快取策略,即在記憶體中會有一份SharedPreferences檔案的快取,因此在多程序模式中,系統對它的讀寫 就變得不可靠,當面對高併發的讀寫訪問,SharedPreferences有很大機率會丟失資料,因此,不建議在程序間通訊中使用SharedPreferences
使用Messenger
Messenger 可以翻譯為信使,通過它可以在不同程序中傳遞Message物件,在Message中放入我們需要傳遞的資料,就可以輕鬆的實現資料的程序間傳遞了,Messenger是一種輕量級的IPC方案,它的底層實現是AIDL,我們大致看一下Messenger這個類的構造方法就明白了

Messenger的使用方法很簡單,它對AIDL做了封裝,使得我們可以更簡單的進行程序間通訊,同時,由於它一次處理一個請求,因此在服務端我們不用考慮執行緒同步的問題,還是因為服務端中不存在併發執行的情形,實現一個Messenger有如下步驟,分為服務端和客戶端:
1 服務端程序
首先我們需要在服務端建立一個Service來處理客戶端的連線請求,同時建立一個Handler 並通過它來建立一個Messenger物件,然後在Service的onBind中返回這個Messenger物件底層的Binder即可
2 客戶端程序
客戶端程序中,首先要繫結服務端的Service,繫結成功後用服務端返回的IBinder 物件建立一個Messenger,通過這個Messenger就可以向服務端傳送訊息了,發訊息型別為Message物件,如果需要服務端能夠迴應客戶端,就和服務端一樣,我們還需要建立一個Handler 並建立一個新的Messenger,並把這個Messenger 物件通過Message的replyTo引數傳遞給服務端,服務端通過這個replyTo引數就可以迴應客戶端。

客戶端實現

在Messenger中進行資料傳遞必須將資料放入Message中,而Messenger和Message都實現了Parcelable介面,因此可以跨程序傳輸,簡單來說,Message中所支援的資料型別就是Messenger 所支援的傳輸型別,實際上,通過Messenger來傳輸Message,Message中能使用的載體使用what,arg1 ,arg2 ,Bundle以及relpyTo,Message中的另一個欄位object 在同一個程序中是很實用的,但是在程序間通訊的時候,在Android 以後Object欄位不支援跨程序通訊,我們自定義的Parcelable物件是無法通過object 欄位來傳輸的,這也導致了object 欄位的實用性大大降低。

使用AIDL
Messenger是以序列的方式處理客戶端發來的訊息,如果大量的訊息同時傳送到服務端,服務端仍然只能一個個處理,如果大量的訊息同時傳送到服務端,服務端仍然只能一個個處理,如果有大量的併發請求,那麼用Messenger就不太合適了,同時,Messenger的作用主要是為了傳遞訊息,很對時候我們可能需要跨程序呼叫服務端的方法,這種情形用Messenger就無法做到了,但是我們可以使用AIDL來實現跨程序的方法呼叫,AIDL也是Messenger的底層實現,因此Messenger本質上也是AIDL,只不過系統為我們做了封裝從而方便上層的呼叫而已。先介紹使用AIDL來進行程序間 通訊的流程:
1 服務端
服務端首先要建立一個Service用來監聽客戶端的連線請求,然後建立一個AIDL檔案,將暴漏給客戶端的介面在這個AIDL檔案中宣告,最後在Service中實現這個AIDL介面即可
2 客戶端
客戶端所要做事情就稍微簡單一些,首先需要繫結服務端的Service,繫結成功後,將服務端返回的Binder物件轉成AIDL介面所屬的型別,接著就可以呼叫AIDL中的方法了
3 AIDL介面的建立
首先是AIDL介面的建立,我們建立了一個字尾為AIDL的檔案,在裡面聲明瞭一個介面和兩個介面的方法
在AIDL檔案中,並不是所有的資料型別都是可以使用的,那麼到底AIDL檔案支援那些資料型別呢?
1 基本資料型別(int long char boolean doubel等)
2 String和CharSequence
3 List :只支援ArrayList,裡面每個元素都必須能夠被AIDL支援
4 Map: 只支援HashMap,裡面的每個元素都必須被AIDL支援,包括key 和value
5 Parcelable:所有實現了Parcelable介面的物件
6 AIDL:支援所有AIDL介面本身也可以在AIDL檔案中使用
以上6種資料型別就是AIDL所支援的所有型別,其中自定義的Parcelable物件和AIDL物件必須要顯示import進來,不管它們是否和當前的AIDL檔案位於同一個包內,比如IBookManager.aidl這個檔案,裡面用到了Book這個類,這個類實現了Parcelable介面並且和IBookManager.aidl位於同一個包中,但是遵守AIDL的規範,我們仍要顯示的import進來。
如果AIDL檔案中用到了自定義的Parcelable物件,那麼必須新建一個和它同名的AIDL檔案,並在其中宣告它為Parcelable型別。
AIDL中每個實現了Parcelable介面的類都需要按照上面那種方式去建立相應的AIDL檔案並宣告那個類為parcelable ,除此之外,AIDL中除了基本嫩資料型別,其他型別的引數必須標上方向:in,out 或者inout,in表示輸入型引數,out表示輸出型引數,inout表示輸入輸出型引數。
使用ContentProvider
ContentProvider是Android 中提供的專門用於不同應用間進行資料共享的方式,從這一點來看,它天生就適合程序間通訊,和Massenger一樣,ContentProvider的底層實現同樣也是Binder,由此可見,Binder 在Android 系統中是何等的重要,雖然ContentProvider的底層實現是Binder,但是她的使用過程要比AIDL簡單得多,這是因為系統已經為我們做了封裝,使得我們無須關心底層細節即可輕鬆實現IPC,ContentProvider 雖然使用起來很簡單,包括自己建立一個ContentProvider 也不是難事,儘管如此,它的細節還是相當多,比如CRUD操作,防止SQL注入和許可權控制等
ContentProvider主要以表格的形式來組織資料,並且可以包含多個表,對於每個表格來說,他們具有列和行的層次性,行往往對應一條資料,而列對應一條記錄中的一個欄位,這點和資料庫很類似,除了表格的形式不同,ContentProvider還支援檔案資料,比如圖片,視訊等,檔案資料和表格資料的結構不同,因此處理這類資料時可以在ContentProvider中返回檔案的控制代碼給外界從而讓檔案來訪問ContentProvider。
使用Socket
Socket也稱為套接字,是網路通訊中的概念,它分為流式套接字和使用者資料報套接字兩種,分別對應網路的傳輸控制層中的TCP和UDP協議,TCP協議是面向連線的協議,提供穩定的雙向通訊功能,TCP連線的建立需要經過三次握手才能完成,為了提供穩定的資料傳輸功能,其本身提供了超時重傳機制,因此具有很高的穩定性,而UDP是無連線的,提供不穩定的單項通訊功能,當然UDP也可以實現雙向通訊功能,在效能上,UDP具有更好的效率,其缺點是不保證資料一定能夠正確傳輸,尤其是在網路擁塞的情況下。兩個程序可以通過Socket來實現資訊的傳輸,Socket本身可以支援傳輸任意位元組流
使用Scoket來進行通訊,有兩點需要注意,首先要宣告許可權

首先要注意的是不能在主執行緒中訪問網路,這會導致我們的程式無法的Android4.0 以上的裝置中執行,而且程序網路操作是耗時的,如果放在主執行緒會影響程式的響應效率,從這方面來說,也不應該在主執行緒中訪問網路
服務端:



客戶端:


