《Android開發藝術探索》筆記:第二章 IPC機制

(一)Android IPC簡介

1.IPC是什麼?

​ 進程間通訊或者跨進程通訊,兩個進程間進行數據交互的一個過程。java

2.進程與線程之間的關係?

線程是CPU調度的最小單元。android

而進程通常指一個執行單元,在PC和移動設備上指一個程序或者一個應用。一個進程能夠包含多個線程,所以進程和線程是包含與被包含的關係。git

補充:進程的五種優先級

  • 前臺進程
    • 即用戶當前操做所必需的進程。若是一個進程知足如下任一條件,即視爲前臺進程:
      • 託管用戶正在交互的 Activity(已調用 Activity 的 onResume() 方法)
      • 託管某個 Service,該Service綁定到用戶正在交互的 Activity
      • 託管正在前臺運行的 Service(服務已調用 startForeground())
      • 託管正在執行生命週期回調的 Service(onCreate()、onStart() 或 onDestroy())
      • 託管正執行其 onReceive() 方法的 BroadcastReceiver
    • 一般,在任意給定時間前臺進程都爲數很少,只有在內存不足以支持它們同時繼續運行這一狀況下系統纔會終止它們。 此時,設備每每已達到內存分頁狀態,所以須要終止一些前臺進程來確保用戶界面正常響應
  • 可見進程
    • 沒有任何前臺組件,但仍會影響用戶在屏幕上所見內容的進程。若是一個進程知足如下任一條件,即視爲可見進程:
      • 託管不在前臺、但仍對用戶可見的 Activity(已調用其 onPause() 方法)。例如,若是前臺 Activity 啓動了一個覆蓋一部分屏幕的對話框時,就會出現這種狀況
      • 託管綁定到可見或前臺Activity 的 Service
    • 可見進程被視爲是極其重要的進程,除非爲了維持全部前臺進程同時運行而必須終止,不然系統不會終止這些進程
  • 服務進程
    • 正在運行已使用 startService() 方法啓動的服務且不屬於上述兩個更高類別的進程。
    • 儘管服務進程與用戶所見內容沒有直接關聯,可是它們一般在執行一些用戶關心的操做(例如,在後臺播放音樂或從網絡下載數據)。所以,除非內存不足以維持全部前臺進程和可見進程同時運行,不然系統會讓服務進程保持運行狀態
  • 後臺進程
    • 包含目前對用戶不可見的 Activity 的進程(已調用 Activity 的 onStop() 方法)。
    • 這些進程對用戶體驗沒有直接影響,系統可能會隨時終止它們以回收內存供前臺進程、可見進程或服務進程使用。 一般會有不少後臺進程在運行,所以它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最後一個被終止。若是某個 Activity 正確實現了生命週期方法,並保存了其當前狀態,則終止其進程不會對用戶體驗產生明顯影響,由於當用戶導航回該 Activity 時,Activity 會恢復其全部可見狀態
  • 空進程
    • 不含任何活動應用組件的進程。
    • 保留這種進程的的惟一目的是用做緩存,以縮短下次在其中運行組件所需的啓動時間。爲使整體系統資源在進程緩存和底層內核緩存之間保持平衡,系統每每會終止這些進程

3.什麼是ANR應用無響應?

主線程中去執行耗時任務,就會形成界面沒法響應(ANR),嚴重影響用戶體驗。github

解決方法:把一些耗時的任務放在子線程中便可。shell

4.不一樣平臺的IPC機制。

Windows上:剪貼板、管道和郵槽等;數組

Linux上:命名管道,共享內容、信號量等;緩存

Android:Binder實現進程間通訊,Socket也能夠實現任意兩個終端之間的通訊。bash

5.多進程通訊的應用場景?

  1. ContentProvider;
  2. 多進程應用:單進程容易受到內存限制而OOM,部分模塊好比WebView能夠單獨做爲一個進程

(二)Android中的多進程模式

使用android:process屬性能夠輕易開啓多進程模式;但多進程未必比單個進程好。服務器

(1)開啓多進程模式

Android中多進程是指一個應用中存在多個進程的狀況,通常只有給四大組件(Activity、BroadcastReceiver、Service、Receiver)指定android:process一種方法。網絡

2.1.1相關代碼:

<activity android:name=".MainActivity">
     <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
 </activity>
<activity android:name=".Main2Activity" android:process=":remote"/>
<activity android:name=".Main3Activity" android:process = "com.example.hzk.myapplication1.remote"/>
複製代碼

2.1.2 效果:

​ 使用DDMS視圖或者命令能夠查看:adb shell ps | findstr com.example.hzk.myapplication1 ​ 前提是通訊:

img

2.1.3 注意事項:

​ Main2Activity和Main3Activity的「:」和「.」的區別:

(1)「:」是簡寫;「.」須要使用全稱,包括包名。

(2)」:」開頭的屬當前應用的私有進程,其餘應用的組件不能夠和它跑在同一個進程中,而進程名不以」:」開頭的進程屬於全局進程,其餘應用經過ShareUID方式能夠和它跑在同一進程中。 每一個應用有一個惟一的UID,具備相同UID的應用才能共享數據。兩個應用經過ShareUID跑在同一個進程由是有要求的,須要這兩個應用有相同的ShareUID而且簽名相同才能夠。分享的信息包括:data目錄、組件信息等。

(2)多進程模式的運行機制

2.2.2多進程存在的問題

  1. 靜態成員和單例模式徹底失效(每一個進程都分配一個獨立的虛擬機,有不一樣的地址空間,因而有不一樣的對象備份,而不是同一份);
  2. 線程同步機制徹底失效(不是一塊內存,因此不一樣進程鎖住的不是同一個對象);
  3. SharedPreferences的可靠性降低(不支持兩個進程同時執行寫操做)。
  4. Application會屢次建立(兩個不一樣的虛擬機和Application的)。

2.2.3實現跨進程通訊的方式

  1. Intent來傳遞數據,
  2. 共享文件和SharedPreferences,
  3. 基於Binder的Messenger和AIDL
  4. Socket

(三)IPC基礎概念介紹

  • Serializable和Parcelable能夠完成對象的序列化過程,當咱們須要經過Intent和Binder傳輸數據時,或者在對象持久化到存儲設備上或者經過網絡傳輸給其餘客戶端就須要使用序列化。
  • 序列化是將對象的狀態信息轉換爲能夠存儲或傳輸的二進制字節流形式的過程。

(1)Serializable接口

Serializable是java中提供的一個序列化接口,它是一個空接口,對象實現這個Serializable接口,就標記這個對象是可序列化的

serialVersioUID做用:

​ 工做機制:序列化的時候會把當前類的serialversionUID寫進序列化的文件中,當反序列化的時候系統會去檢測文件中serialversionUID,看它是否和當前類的serialversionUID一致,若是一致就說明序列化的類的版本和當前類的版本是相同的,這個時候能夠成功反序列化;不然就說明當前類和序列化的類相比發生了某些變換,好比成員,類型可能發生了變化,這個時候是沒法正常的反序列化的。

  • 當版本升級後,咱們可能刪除了某個變量或者新增長了一些新的成員變量,只要serialVersionUID不變,這個時候咱們的反向序列化過程仍然可以成功,程序仍然可以最大限度的恢復數據。

  • 但如修改了類名,修改了成員常量的類型,即便serialVersionUID經過驗證,也會反序列化失敗。

  • 若是沒有手動指定serialVersionUID,系統也會根據當前類的結構生成它的hahs值。

  • 須要注意的是:

    (1)靜態成員變量屬於類不屬於對象,因此不會參與序列化過程;

    (2)transient關鍵字標記的成員變量不參與序列化過程。

(2)Parcelable接口

​ Parcelable是一個接口,一個類的對象就能夠實現序列化並能夠經過Intent和Binder傳遞。 3.2.1代碼實現:

public class User implements Parcelable{
    public int userId;
    public String userName;
    public boolean isMale;
    protected User(int userId,String userName,boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale  = isMale;
    }
    //序列化
        @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(userId);
        out.writeString(userName);
        out.writeInt(isMale?1:0);

    }
    
    //反序列化
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
    public User(Parcel in) {
        userId = in.readInt();
        userName =in.readString();
        isMale = in.readInt() ==1;
    }
    @Override
    public int describeContents() {
        return 0;//通常返回0,若是含有文件描述符返回1
    }

}
複製代碼

3.2.2每一個實現方法的功能: 序列化功能由writeToParcel方法來完成,最終是經過Parcel中的一系列write方法來完成的,

反序列化功能由CREATOR來完成,其內部標明瞭如何建立序列化對象和數組,並經過Parcel的一系列read方法來完成反序列化過程。User方法從序列化後的對象中建立原始對象。

在Book(Parcel in)方法中,若是有一個成員變量是另外一個可序列化對象,在反序列化過程當中須要傳遞當前線程的上下文類加載器,不然會報沒法找到類的錯誤。

book = in.readParcelable(Thread.currentThread().getContextClassLoader());
複製代碼

這裏我推薦一個插件android-parcelable-intellij-plugin,專門用於自動生成這些重複樣本代碼。

Parcelable和Serializable如何選擇?

  • 二者最大的區別在於 存儲媒介的不一樣,Serializable 使用 I/O 讀寫存儲在硬盤上,而 Parcelable 是直接 在內存中讀寫。很明顯,內存的讀寫速度一般大於 IO 讀寫,因此在 Android 中傳遞數據優先選擇 Parcelable
  • Serializable 會使用反射,序列化和反序列化過程須要大量 I/O 操做, Parcelable 自已實現封送和解封(marshalled &unmarshalled)操做不須要用反射,數據也存放在 Native 內存中,效率要快不少。
  • Parcelable 的性能比 Serializable 好,在內存開銷方面較小,因此在內存間數據傳輸時推薦使用 Parcelable(如 Activity 間傳輸數據)。
  • 而 Serializable 可將數據持久化方便保存,因此在須要保存或網絡傳輸數據時選擇 Serializable,由於 Android 不一樣版本 Parcelable 可能不一樣,因此不推薦使用 Parcelable進行數據持久化.

2.3.3 Binder

Binder是Android中的一個類,實現了 IBinder 接口。從IPC角度說,Binder是Andoird的一種跨進程通信方式,Binder還能夠理解爲一種虛擬物理設備,它的設備驅動是/dev/binder。從Android Framework角度來講,Binder是 ServiceManager 鏈接各類Manager( ActivityManager· 、 WindowManager )和相應 ManagerService 的橋樑。從Android應用層來講,Binder是客戶端和服務端進行通訊的媒介,當bindService時,服務端返回一個包含服務端業務調用的Binder對象,經過這個Binder對象,客戶端就能夠獲取服務器端提供的服務或者數據( 包括普通服務和基於AIDL的服務)。

img
Binder通訊採用C/S架構,從組件視角來講,包含Client、Server、ServiceManager以及binder驅動,其中ServiceManager用於管理系統中的各類服務。 圖中的Client,Server,Service Manager之間交互都是虛線表示,是因爲它們彼此之間不是直接交互的,而是都經過與Binder驅動進行交互的,從而實現IPC通訊方式。其中Binder驅動位於內核空間,Client,Server,Service Manager位於用戶空間。Binder驅動和Service Manager能夠看作是Android平臺的基礎架構,而Client和Server是Android的應用層,開發人員只需自定義實現client、Server端,藉助Android的基本平臺架構即可以直接進行IPC通訊。

Android中Binder主要用於 Service ,包括AIDL和Messenger。普通Service的Binder不涉及進程間通訊,Messenger的底層實際上是AIDL,因此下面經過AIDL分析Binder的工做機制。

由系統根據AIDL文件自動生成.java文件

  • Book.java 表示圖書信息的實體類,實現了Parcelable接口。
  • Book.aidl Book類在AIDL中的聲明。
  • IBookManager.aidl 定義的管理Book實體的一個接口,包含 getBookList 和 addBook 兩個方法。儘管Book類和IBookManager位於相同的包中,可是在IBookManager仍然要導入Book類。
  • IBookManager.java 系統爲IBookManager.aidl生產的Binder類,在 gen 目錄下。

IBookManager繼承了 IInterface 接口,全部在Binder中傳輸的接口都須要繼IInterface接口。結構以下:

  • 聲明瞭 getBookList 和 addBook 方法,還聲明瞭兩個整型id分別標識這兩個方法,用於標識在 transact 過程當中客戶端請求的究竟是哪一個方法。
  • 聲明瞭一個內部類 Stub ,它繼承自Binder,而且是一個抽象類,並無實現getBookList 和 addBook 方法,這兩個方法須要服務端new Stub的時候去實現。
    • 當客戶端和服務端位於同一進程時,方法調用不會走跨進程的 transact 。
    • 當兩者位於不一樣進程時,方法調用須要走 transact 過程,就會調用到 Stub 的內部代理類 Proxy中的方法
  • Stub 的內部代理類 Proxy,由客戶端來使用。它實現了IBookManager接口,而且實現了 getBookList 和 addBook 方法,可是裏面只是把數據裝進data這個Parcel對象,經過mRemote的transact方法發送給服務端,接着用reply這個Parcel對象等待服務端數據的返回,這一切都是經過mRemote這個IBinder對象進行,mRemote表明着Binder對象的本地代理,mRemote會經過Binder驅動來完成與遠程服務端的Stub的通訊。
  • 這個接口的核心實現就是它的內部類 Stub 和 Stub 的內部代理類 Proxy 。

Stub和Proxy類的內部方法和定義

  • DESCRIPTOR Binder的惟一標識,通常用Binder的類名錶示。

  • asInterface(android.os.IBinder obj) 將服務端的Binder對象轉換爲客戶端所需的AIDL接口類型的對象,若是C/S位於同一進 程,此方法返回就是服務端的Stub對象自己,不然返回的就是系統封裝後的Stub.proxy對 象。因此,跨進程通訊中客戶端是使用代理對象來實現通訊的。

  • asBinder 返回當前Binder對象。

  • onTransact 這個方法運行在服務端的Binder線程池中,由客戶端發起跨進程請求時,遠程請求會經過 系統底層封裝後交由此方法來處理。該方法的原型是

    java public Boolean onTransact(int code,Parcelable data,Parcelable reply,int flags)
    複製代碼
    • 服務端經過code肯定客戶端請求的目標方法是什麼,
    • 接着從data取出目標方法所需的參數,而後執行目標方法。
    • 執行完畢後向reply寫入返回值( 若是有返回值) 。
    • 若是這個方法返回值爲false,那麼服務端的請求會失敗,利用這個特性咱們能夠來作權限驗證。
  • Proxy#getBookList 和Proxy#addBook 這兩個方法運行在客戶端,內部實現過程以下:

    • 首先建立該方法所須要的輸入型對象Parcel對象_data,輸出型Parcel對象_reply和返回值對象List。
    • 而後把該方法的參數信息寫入_data( 若是有參數)_
    • _接着調用transact方法發起RPC( 遠程過程調用) ,同時當前線程掛起
    • 而後服務端的onTransact方法會被調用知道RPC過程返回後,當前線程繼續執行,並從_reply中取出RPC過程的返回結果,最後返回_reply中的數據。

AIDL文件不是必須的,之因此提供AIDL文件,是爲了方便系統爲咱們生成IBookManager.java,但咱們徹底能夠本身寫一個。

Binder的工做機制

img

  • 因爲當前線程會被掛起知道服務端進程返回數據,因此遠程請求若是很耗時,則不能在UI線程在發起;
  • Binder方法須要用同步的方法去實現,由於他已經運行在一個線程中了。

linkToDeath和unlinkToDeath

若是服務端進程異常終止,咱們到服務端的Binder鏈接斷裂。可是,若是咱們不知道Binder鏈接已經斷裂,那麼客戶端功能會受影響。經過linkTODeath咱們能夠給Binder設置一個死亡代理,當Binder死亡時,咱們就會收到通知。

  1. 首先,聲明一個 DeathRecipient 對象。 DeathRecipient 是一個接口,只有一個方法 binderDied ,當Binder死亡的時候,系統就會回調 binderDied 方法,而後咱們就能夠從新綁定遠程服務。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
@Override
public void binderDied(){
    if(mBookManager == null){
    return;
} 
    mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
    mBookManager = null;
    // TODO:這裏從新綁定遠程Service
	}
}
複製代碼

而後,在客戶端綁定遠程服務成功後,給binder設置死亡代理:

mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient,0);
複製代碼
  1. 另外,能夠經過Binder的 isBinderAlive 判斷Binder是否死亡。

2.4 Android中的IPC方式

主要有如下方式:

  • Intent中附加extras
  • 共享文件
  • Binder
  • ContentProvider
  • Socket

2.4.1 使用Bundle

  • 四大組件中的三大組件( Activity、Service、Receiver) 都支持在Intent中傳遞 Bundle 數據。
  • Bundle實現了Parcelable接口,所以能夠方便的在不一樣進程間傳輸。
  • 當咱們在一個進程中啓動了另外一個進程的Activity、Service、Receiver,能夠再Bundle中附加咱們須要傳輸給遠程進程的消息並經過Intent發送出去。被傳輸的數據必須可以被序列化。

可是在Intent 傳輸數據的過程當中,用到了 Binder,Intent中的數據,即Bundle數據,會做爲 Parcel 存儲在Binder 的事務緩衝區(Binder transaction buffer)中的對象進行傳輸,而這個 Binder 事務緩衝區具備一個有限的固定大小,約爲1MB,並且這個1Mb並非當前進程所獨享,而是全部進程共享的,因此因爲1Mb的限制,Bundle不能存放大量的數據,否則會報TransactionTooLargeException,而且Bundle中存放的數據也要求可以被序列化,因此Bundle只適用於數據量小和簡單的進程間通訊。

2.4.2 使用文件共享

  • 咱們能夠序列化一個對象到文件系統中的同時從另外一個進程中恢復這個對象。
  • 經過 ObjectOutputStream / ObjectInputStream 序列化一個對象到文件中,或者在另外一個進程從文件中反序列這個對象。注意:反序列化獲得的對象只是內容上和序列化以前的對象同樣,本質是兩個對象
  • Android基於Linux,文件支持併發讀寫。而文件併發讀寫會致使讀出的對象可能不是最新的 。因此文件共享方式適合對數據同步要求不高的進程之間進行通訊,而且要妥善處理併發讀寫問題。
  • SharedPreferences 底層實現採用XML文件來存儲鍵值對。系統對它的讀/寫有必定的緩存策略,即在內存中會有一份 SharedPreferences 文件的緩存,所以在多進程模式下,系統對它的讀/寫變得不可靠,面對高併發讀/寫時 SharedPreferences 有很大概率丟失數據,所以不建議在IPC中使用 SharedPreferences

2.4.3 使用Messenger

  • Messenger能夠在不一樣進程間傳遞Message對象。是一種輕量級的IPC方案,底層實現是AIDL。它對AIDL進行了封裝,使得咱們能夠更簡便的進行IPC。
  • 具體使用時,分爲服務端和客戶端:
  1. 服務端:建立一個Service來處理客戶端請求,同時建立一個Handler並經過它來建立一個 Messenger,而後在Service的onBind中返回Messenger對象底層的Binder便可。 private final Messenger mMessenger = new Messenger (new xxxHandler());
  2. 客戶端:綁定服務端的Sevice,利用服務端返回的IBinder對象來建立一個Messenger,經過這個Messenger就能夠向服務端發送消息了,消息類型是 Message 。若是須要服務端響應,則須要建立一個Handler並經過它來建立一個Messenger( 和服務端同樣) ,並經過 Message 的 replyTo 參數傳遞給服務端。服務端經過Message的 replyTo 參數就能夠迴應客戶端了。
  • 總而言之,就是客戶端和服務端 拿到對方的Messenger來發送 Message 。只不過客戶端經過bindService 而服務端經過 message.replyTo 來得到對方的Messenger。
  • Messenger在 Hanlder 以串行的方式處理隊列中的消息,因此不存在併發執行,所以咱們不用考慮線程同步的問題。
  • 缺點:
    • Messenger是串行處理消息的,不適合大量併發請求
    • Messenger用來傳遞消息,沒法跨進程調用服務端的方法。

2.4.4 使用AIDL

AIDL 意思即 Android Interface Definition Language,翻譯過來就是Android接口定義語言,是用於定義服務端和客戶端通訊接口的一種描述語言,能夠拿來生成用於 IPC 的代碼。 使用步驟以下:

  1. 服務端須要建立Service來監聽客戶端請求,而後建立一個AIDL文件,將暴露給客戶端的接口在AIDL文件中聲明,最後在Service中實現這個AIDL接口便可。
  2. 客戶端首先綁定服務端的Service,綁定成功後,將服務端返回的Binder對象轉成AIDL接口所屬的類型,接着就能夠調用AIDL中的方法了。

AIDL支持的數據類型:

  • 基本數據類型、String、CharSequence
  • List:只支持ArrayList,裏面的每一個元素必須被AIDL支持
  • Map:只支持HashMap,裏面的每一個元素必須被AIDL支持
  • Parcelable
  • 全部的AIDL接口自己也能夠在AIDL文件中使用

自定義的Parcelable對象和AIDL對象,無論它們與當前的AIDL文件是否位於同一個包,都必須顯式import進來。 若是AIDL文件中使用了自定義的Parcelable對象,就必須新建一個和它同名的AIDL文件,並在其中聲明它爲Parcelable類型。

package com.ryg.chapter_2.aidl;
parcelable Book;
複製代碼

AIDL接口中的參數除了基本類型之外都必須代表方向in/out。

AIDL接口文件中只支持方法,不支持聲明靜態常量。

建議把全部和AIDL相關的類和文件放在同一個包中,方便管理。

  • AIDL方法是在服務端的Binder線程池中執行的,所以當多個客戶端同時鏈接時,管理數據的集合直接採用 CopyOnWriteArrayList 來進行自動線程同步,只要這個集合類實現了List接口就可使用,不必定非要是ArrayList,可是Binder中仍是會造成一個ArrayList。相似的還有 ConcurrentHashMap 。
  • 由於客戶端的listener和服務端的listener不是同一個對象,因此 RecmoteCallbackList 是系統專門提供用於刪除跨進程listener的接口,支持管理任意的AIDL接口。它內部經過一個Map接口來保存全部的AIDL回調,這個Map的key是 IBinder 類型,value是 Callback 類型。當客戶端解除註冊時,遍歷服務端全部listener,找到和客戶端listener具備相同Binder對象的服務端listenr並把它刪掉。
  • 客戶端調用遠程方法的時候線程會被掛起,因爲被調用的方法運行在服務端的Binder線程池中,若是很耗時,就會讓客戶端一直等待甚至ANR,因此客戶端不能在主線程中去調用服務端的方法,要開啓一個子線程。同理,服務端調用客戶端的listener的方法時,調用的方法運行在客戶端的Binder線程池中,因此不能在這裏作UI操做,除非切換到UI線程。
  • Binder可能會意外死亡,若意外中止,則重連服務。
    • 方法一:給Binder設置DeathRecipient監聽,Binder死亡,收到Binderdied回調,在binderDied中咱們能夠重連遠程服務。
    • 方法二:onServiceDisconnected中進行重連操做。
  • 權限驗證 默認狀況下,咱們的遠程服務任何人均可以鏈接,咱們必須加入權限驗證功能,權限驗證失敗則沒法調用服務中的方法。一般有兩種驗證方法:
    • 在onBind中驗證,驗證不經過返回null。 驗證方式好比permission驗證,在AndroidManifest聲明Android自定義權限和使用權限。這種方法也適用於Messager。
    • 在服務端的onTransact中驗證,驗證不經過返回false。能夠permission驗證,還能夠驗證包名。

2.4.5 使用ContentProvider

  • ContentProvider是四大組件之一,天生就是用來進程間通訊。和Messenger同樣,其底層實現是用Binder
  • 系統預置了許多ContentProvider,好比通信錄、日程表等。要RPC訪問這些信息,只須要經過ContentResolver的query、update、insert和delete方法便可。
  • 建立自定義的ContentProvider,只需繼承ContentProvider類並實現 onCreate 、 query 、 update 、 insert 、 getType 六個抽象方法便可。getType用來返回一個Uri請求所對應的MIME類型,剩下四個方法對應於CRUD操做。這六個方法都運行在ContentProvider進程中,除了 onCreate 由系統回調並運行在主線程裏,其餘五個方法都由外界調用並運行在Binder線程池中。
  • ContentProvider是經過Uri來區分外界要訪問的數據集合,例如外界訪問ContentProvider中的表,咱們須要爲它們定義單獨的Uri和Uri_Code。根據Uri_Code,咱們就知道要訪問哪一個表了。
  • query、update、insert、delete四大方法存在多線程併發訪問,所以方法內部要作好線程同步。
  • 若採用SQLite而且只有一個SQLiteDatabase,SQLiteDatabase內部已經作了同步處理。如果多個SQLiteDatabase或是採用List做爲底層數據集,就必須作線程同步。

2.4.6 使用Socket

  • Socket也稱爲「套接字」,分爲流式套接字和用戶數據報套接字兩種,分別對應於TCP和UDP協議。Socket能夠實現計算機網絡中的兩個進程間的通訊,固然也能夠在本地實現進程間的通訊。。
  • 在遠程Service創建一個TCP服務,而後在主界面中鏈接TCP服務。服務端Service監聽本地端口,客戶端鏈接指定的端口,創建鏈接成功後,拿到 Socket 對象就能夠向服務端發送消息或者接受服務端發送的消息。
  • 除了採用TCP套接字,也能夠用UDP套接字。實際上socket不只能實現進程間的通訊,還能夠實現設備間的通訊(只要設備之間的IP地址互相可見)。
  • 可是它的傳輸效率也是很是的低

2.5 Binder鏈接池

前面提到AIDL的流程是:首先建立一個service和AIDL接口,接着建立一個類繼承自AIDL接口中的Stub類並實現Stub中的抽象方法,客戶端在Service的onBind方法中拿到這個類的對象,而後綁定這個service,創建鏈接後就能夠經過這個Stub對象進行RPC。 那麼若是項目龐大,有多個業務模塊都須要使用AIDL進行IPC,隨着AIDL數量的增長,咱們不能無限制地增長Service,咱們須要把全部AIDL放在同一個Service中去管理。 服務端只有一個Service,把全部AIDL放在一個Service中,不一樣業務模塊之間不能有耦合 服務端提供一個 queryBinder 接口,這個接口可以根據業務模塊的特徵來返回響應的Binder對象給客戶端 不一樣的業務模塊拿到所需的Binder對象就能夠進行RPC了

img

2.6 選用合適的IPC方式

img
相關文章
相關標籤/搜索