Android多程式通訊之幾個基本問題

xxq2dream發表於2018-09-22

開啟多程式的方法

Android 中使用多程式只有一種方法,那就是給四大元件(Activity、Service、Receiver、ContentProvider)在AndroidMenifest中指定android:process屬性

<service
    android:name="com.xxq2dream.service.BookManagerService"
    android:process=":remote" />
  • 通過如上的設定,BookManagerService就執行在獨立的程式下,程式名為:包名:remote,比如:com.xxq2dream.android_ipc:remote
  • 上面的設定方法表示remote程式是當前應用的私有程式,其他應用的元件不可以和它跑在同一個程式中
  • android:process屬性的設定還有另一種方式com.xxq2dream.android_ipc.remote,這種方式的程式是全域性程式,其他應用通過ShareUID方式可以和它跑在同一個程式中。
  • 一般我們在應用中用的比較多的是第一種,也即是應用的私有程式

開啟多程式模式後的問題

Android為每個程式都分配一個獨立的虛擬機器,不同的虛擬機器在記憶體分配上有不同的地址空間,這就導致了不同的虛擬機器中訪問同一個類的物件會產生多個副本。一個應用間的多程式可以理解為就相當於2個不同的應用採用了SharedUID的模式。

  • 所有執行在不同的程式中的四大元件,只要它們之間需要通過記憶體來共享資料,都會共享失敗
  • 靜態成員和單例模式完全失效(不同程式的記憶體區域都不一樣了)
  • 執行緒同步機制完全失效(不同的程式鎖的都不是同一個物件)
  • Application會多次建立
  • SharedPreferce的可靠性下降(SharedPreferce底層是讀寫xml檔案實現的,系統對它的讀寫有一定的快取策略,在記憶體中會有一份SharedPreferce檔案的快取,所以多個程式併發寫操作可能導致資料丟失)

多程式通訊的實現方式

使用Bundle
  • 四大元件中的三大元件(Activity、Service、Receiver)都支援在Intent中傳遞Bundle資料
  • Bundle中的資料除了基本型別,其他的都需要可序列化
  • 適用於從一個程式啟動另一個程式,比如在一個程式中啟動另一個程式中的Activity、Service、Receiver,與此同時傳遞資料的情況
使用檔案共享
  • 2個程式通過讀寫同一個檔案來交換資料
  • 當然要把資料寫入檔案,必然要求資料可以序列化和反序列化
  • 檔案共享對檔案格式沒有要求,只要讀寫雙方約定好資料格式
  • 檔案共享的方式存在併發讀寫的問題,適合對資料同步要求不高的程式間通訊,並且要妥善處理併發讀寫的問題
使用Messager
  • 使用Messager來傳遞Message,Message中能使用的欄位只有what、arg1、arg2、Bundle和replyTo,自定義的Parcelable物件無法通過object欄位來傳輸
  • Message中的Bundle支援多種資料型別,replyTo欄位用於傳輸Messager物件,以便程式間相互通訊
  • Messager以序列的方式處理客戶端發來的訊息,不適合有大量併發的請求
  • Messager方法只能傳遞訊息,不能跨程式呼叫方法
使用AIDL
  • AIDL介面可以通過編寫AIDL檔案然後由系統生成對應的Binder類,通過Binder我們就可以進行跨程式通訊了
  • AIDL檔案中只支援如下幾種型別:

    • 基本型別,如int、long等
    • String和CharSequence
    • List:只支援ArrayList,裡面的每個元素都必須被AIDL支援
    • Map:只支援HashMap,裡面的key、value都必須被AIDL支援
    • AIDL:所有的AIDL介面本身也可以在AIDL檔案中使用
  • AIDL檔案中用到的自定義Parcelable物件和AIDL物件必須要顯示的import進來
  • 出了基本資料型別,其他型別的引數必須標明是入參還是出參,in表示輸入型引數,out表示輸出型引數,inout表示輸入輸出型引數

序列化之Serializable介面和Parcelable介面

  • Serializable介面和Parcelable介面都能實現序列化,但是有區別
Serializable介面
  • Serializable介面實現序列化比較簡單,只需要在需要實現序列化的類實現Serializable介面就行
  • serialVersionUID可以指定也可以不指定,不指定的話系統會預設給我們生成
  • serialVersionUID的作用是用於標識當前類的版本,便於在反序列化過程中判斷類是否有更改,如果serialVersionUID不一致,反序列化就會失敗
  • 人為指定serialVersionUID後,如果不是破壞性的改動了類,比如版本升級後增加了一個欄位,那反序列化以後仍然能成功;而如果沒有指定,反序列化就會失敗
  • serialVersionUID是否指定需要根據具體的需要來確定
  • 靜態成員變數和transient關鍵字標記的變數不參與序列化過程
Parcelable介面
  • Parcelable介面實現序列化稍微複雜,需要實現writeToParcel方法、describeContents方法以及生成器Creator
  • 內容描述功能的describeContents方法一般都返回0,噹噹前物件中存在檔案描述符時才返回1
  • Parcelable介面中的Parcel內部包裝了可序列化的資料,可以在Binder中自由傳輸
Parcelable介面和Serializable介面
  • Serializable介面是Java中的序列化介面,使用簡單但是開銷大,序列化和反序列化過程需要大量的I/O操作,一般用於將物件序列化到儲存裝置中或是將物件序列化後通過網路進行傳輸
  • Parcelable介面是Android中的序列化方式,使用稍微麻煩,但是效率很高,是Android中推薦的序列化方式,主要用在記憶體序列化上

歡迎關注我的微信公眾號,和我一起學習一起成長!
AntDream

相關文章