Android的IPC機制和多進(jìn)程開發(fā)模式

導(dǎo)語

對Android中的IPC機制和多進(jìn)程開發(fā)模式有深入的理解。

主要內(nèi)容

  • Android IPC 簡介
  • Android中的多進(jìn)程模式
  • IPC基礎(chǔ)概念介紹
  • Android中的IPC方式
  • Binder連接池
  • 選用合適的IPC方式

具體內(nèi)容

Android IPC 簡介

IPC即Inter-Process Communication,含義為進(jìn)程間通信或者跨進(jìn)程通信,是指兩個進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過程。
線程是CPU調(diào)度的最小單元,是一種有限的系統(tǒng)資源。進(jìn)程一般指一個執(zhí)行單元,在PC和移動設(shè)備上是指一個程序或者應(yīng)用。進(jìn)程與線程是包含與被包含的關(guān)系。一個進(jìn)程可以包含多個線程。最簡單的情況下一個進(jìn)程只有一個線程,即主線程(例如Android的UI線程)。
任何操作系統(tǒng)都需要有相應(yīng)的IPC機制。如Windows上的剪貼板、管道和郵槽;Linux上命名管道、共享內(nèi)容、信號量等。Android中最有特色的進(jìn)程間通信方式就是binder,另外還支持socket。contentProvider是Android底層實現(xiàn)的進(jìn)程間通信。

在Android中,IPC的使用場景大概有以下:

  • 有些模塊由于特殊原因需要運行在單獨的進(jìn)程中。
  • 通過多進(jìn)程來獲取多份內(nèi)存空間。
  • 當(dāng)前應(yīng)用需要向其他應(yīng)用獲取數(shù)據(jù)。

Android中的多進(jìn)程模式

開啟多進(jìn)程模式

在Android中使用多線程只有一種方法:給四大組件在Manifest中指定 android:process 屬性。這個屬性的值就是進(jìn)程名。這意味著不能在運行時指定一個線程所在的進(jìn)程。

<activity android:name=".MainActivity"
            android:process=":remote"/>
<activity android:name=".MainActivity"
            android:process="com.example.c2.remote"/>

兩種進(jìn)程命名方式的區(qū)別:

  • “:remote”
    “:”的含義是指在當(dāng)前的進(jìn)程名前面附加上當(dāng)前的包名,完整的進(jìn)程名為“com.example.c2.remote”。這種進(jìn)程屬于當(dāng)前應(yīng)用的私有進(jìn)程,其他應(yīng)用的組件不可以和它跑在同一個進(jìn)程中。
  • “com.example.c2.remote”
    這是一種完整的命名方式。這種進(jìn)程屬于全局進(jìn)程,其他應(yīng)用可以通過ShareUID方式和它跑在同一個進(jìn)程中。

tips:使用 adb shell ps 或 adb shell ps|grep 包名 查看當(dāng)前所存在的進(jìn)程信息。

多線程模式的運行機制

Android為每個進(jìn)程都分配了一個獨立的虛擬機,不同虛擬機在內(nèi)存分配上有不同的地址空間,導(dǎo)致不同的虛擬機訪問同一個類的對象會產(chǎn)生多份副本。例如不同進(jìn)程的Activity對靜態(tài)變量的修改,對其他進(jìn)程不會造成任何影響。所有運行在不同進(jìn)程的四大組件,只要它們之間需要通過內(nèi)存在共享數(shù)據(jù),都會共享失敗。四大組件之間不可能不通過中間層來共享數(shù)據(jù)。

多進(jìn)程會帶來以下問題:

  • 靜態(tài)成員和單例模式完全失效。
  • 線程同步鎖機制完全失效。
    這兩點都是因為不同進(jìn)程不在同一個內(nèi)存空間下,鎖的對象也不是同一個對象。
  • SharedPreferences的可靠性下降。
    SharedPreferences底層是 通過讀/寫XML文件實現(xiàn)的,并發(fā)讀/寫會導(dǎo)致一定幾率的數(shù)據(jù)丟失。
  • Application會多次創(chuàng)建。
    由于系統(tǒng)創(chuàng)建新的進(jìn)程的同時分配獨立虛擬機,其實這就是啟動一個應(yīng)用的過程。在多進(jìn)程模式中,不同進(jìn)程的組件擁有獨立的虛擬機、Application以及內(nèi)存空間。

多進(jìn)程相當(dāng)于兩個不同的應(yīng)用采用了SharedUID的模式。

實現(xiàn)跨進(jìn)程的方式有很多:

  • Intent傳遞數(shù)據(jù)。
  • 共享文件和SharedPreferences。
  • 基于Binder的Messenger和AIDL。
  • Socket等。

IPC基礎(chǔ)概念介紹

主要介紹 Serializable 、 Parcelable 、 Binder 。Serializable和Parcelable接口可以完成對象的序列化過程,我們通過Intent和Binder傳輸數(shù)據(jù)時就需要Parcelable和Serializable。還有的時候我們需要對象持久化到存儲設(shè)備上或者通過網(wǎng)絡(luò)傳輸?shù)狡渌蛻舳耍残枰猄erializable完成對象持久化。

Serializable接口

Serializable 是Java提供的一個序列化接口( 空接口),為對象提供標(biāo)準(zhǔn)的序列化和反序列化操作。只需要一個類去實現(xiàn) Serializable 接口并聲明一個 serialVersionUID 即可實現(xiàn)序列化。

private static final long serialVersionUID = 8711368828010083044L

serialVersionUID也可以不聲明。如果不手動指定 serialVersionUID 的值,反序列化時如果當(dāng)前類有所改變( 比如增刪了某些成員變量) ,那么系統(tǒng)就會重新計算當(dāng)前類的hash值并更新 serialVersionUID 。這個時候當(dāng)前類的 serialVersionUID 就和序列化數(shù)據(jù)中的serialVersionUID 不一致,導(dǎo)致反序列化失敗,程序就出現(xiàn)crash。

靜態(tài)成員變量屬于類不屬于對象,不參與序列化過程,其次 transient 關(guān)鍵字標(biāo)記的成員變量也不參與序列化過程。

通過重寫writeObject和readObject方法可以改變系統(tǒng)默認(rèn)的序列化過程。

Parcelable接口

Parcel內(nèi)部包裝了可序列化的數(shù)據(jù),可以在Binder中自由傳輸。序列化過程中需要實現(xiàn)的功能有序列化、反序列化和內(nèi)容描述。

序列化功能由 writeToParcel 方法完成,最終是通過 Parcel 的一系列writer方法來完成。

    @Override
    public void writeToParcel(Parcel out, int flags) {
    out.writeInt(code);
    out.writeString(name);
    }

反序列化功能由 CREATOR 來完成,其內(nèi)部表明了如何創(chuàng)建序列化對象和數(shù)組,通過 Parcel 的一系列read方法來完成。

public static final Creator<Book> CREATOR = new Creator<Book>() {
    @Override
    public Book createFromParcel(Parcel in) {
        return new Book(in);
    } 
    @Override
    public Book[] newArray(int size) {
        return new Book[size];
    }
};

protected Book(Parcel in) {
    code = in.readInt();
    name = in.readString();
}

在Book(Parcel in)方法中,如果有一個成員變量是另一個可序列化對象,在反序列化過程中需要傳遞當(dāng)前線程的上下文類加載器,否則會報無法找到類的錯誤。

 book = in.readParcelable(Thread.currentThread().getContextClassLoader());

內(nèi)容描述功能由 describeContents 方法完成,幾乎所有情況下都應(yīng)該返回0,僅當(dāng)當(dāng)前對象中存在文件描述符時返回1。

public int describeContents() {
    return 0;
}

Serializable 是Java的序列化接口,使用簡單但開銷大,序列化和反序列化過程需要大量I/O操作。而 Parcelable 是Android中的序列化方式,適合在Android平臺使用,效率高但是使用麻煩。 Parcelable 主要在內(nèi)存序列化上,Parcelable 也可以將對象序列化到存儲設(shè)備中或者將對象序列化后通過網(wǎng)絡(luò)傳輸,但是稍顯復(fù)雜,推薦使用 Serializable 。

Binder

Binder是Android中的一個類,實現(xiàn)了 IBinder 接口。從IPC角度說,Binder是Android的一種跨進(jìn)程通訊方式,Binder還可以理解為一種虛擬物理設(shè)備,它的設(shè)備驅(qū)動是/dev/binder。從Android Framework角度來說,Binder是 ServiceManager 連接各種Manager(ActivityManager、WindowManager)和相應(yīng) ManagerService 的橋梁。從Android應(yīng)用層來說,Binder是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng)bindService時,服務(wù)端返回一個包含服務(wù)端業(yè)務(wù)調(diào)用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務(wù)器端提供的服務(wù)或者數(shù)據(jù)( 包括普通服務(wù)和基于AIDL的服務(wù))。

IPC-Binder

Binder通信采用C/S架構(gòu),從組件視角來說,包含Client、Server、ServiceManager以及binder驅(qū)動,其中ServiceManager用于管理系統(tǒng)中的各種服務(wù)。

圖中的Client,Server,Service Manager之間交互都是虛線表示,是由于它們彼此之間不是直接交互的,而是都通過與Binder驅(qū)動進(jìn)行交互的,從而實現(xiàn)IPC通信方式。其中Binder驅(qū)動位于內(nèi)核空間,Client,Server,Service Manager位于用戶空間。Binder驅(qū)動和Service Manager可以看做是Android平臺的基礎(chǔ)架構(gòu),而Client和Server是Android的應(yīng)用層,開發(fā)人員只需自定義實現(xiàn)client、Server端,借助Android的基本平臺架構(gòu)便可以直接進(jìn)行IPC通信。

Android中Binder主要用于 Service ,包括AIDL和Messenger。普通Service的Binder不涉及進(jìn)程間通信,Messenger的底層其實是AIDL,所以下面通過AIDL分析Binder的工作機制。

由系統(tǒng)根據(jù)AIDL文件自動生成.java文件
  1. Book.java
    表示圖書信息的實體類,實現(xiàn)了Parcelable接口。
  2. Book.aidl
    Book類在AIDL中的聲明。
  3. IBookManager.aidl
    定義的管理Book實體的一個接口,包含 getBookList 和 addBook 兩個方法。盡管Book類和IBookManager位于相同的包中,但是在IBookManager仍然要導(dǎo)入Book類。
  4. IBookManager.java
    系統(tǒng)為IBookManager.aidl生產(chǎn)的Binder類,在 gen 目錄下。

IBookManager繼承了 IInterface 接口,所有在Binder中傳輸?shù)慕涌诙夹枰^IInterface接口。結(jié)構(gòu)如下:

  • 聲明了 getBookList 和 addBook 方法,還聲明了兩個整型id分別標(biāo)識這兩個方法,用于標(biāo)識在 transact 過程中客戶端請求的到底是哪個方法。
  • 聲明了一個內(nèi)部類 Stub ,這個 Stub 就是一個Binder類,當(dāng)客戶端和服務(wù)端位于同一進(jìn)程時,方法調(diào)用不會走跨進(jìn)程的 transact 。當(dāng)二者位于不同進(jìn)程時,方法調(diào)用需要走 transact 過程,這個邏輯有 Stub 的內(nèi)部代理類 Proxy 來完成。
  • 這個接口的核心實現(xiàn)就是它的內(nèi)部類 Stub 和 Stub 的內(nèi)部代理類 Proxy 。
Stub和Proxy類的內(nèi)部方法和定義
Binder的工作機制
  1. DESCRIPTOR
    Binder的唯一標(biāo)識,一般用Binder的類名表示。
  2. asInterface(android.os.IBinder obj)
    將服務(wù)端的Binder對象轉(zhuǎn)換為客戶端所需的AIDL接口類型的對象,如果C/S位于同一進(jìn)程,此方法返回就是服務(wù)端的Stub對象本身,否則返回的就是系統(tǒng)封裝后的Stub.proxy對象。
  3. asBinder
    返回當(dāng)前Binder對象。
  4. onTransact
    這個方法運行在服務(wù)端的Binder線程池中,由客戶端發(fā)起跨進(jìn)程請求時,遠(yuǎn)程請求會通過系統(tǒng)底層封裝后交由此方法來處理。該方法的原型是:
 java public Boolean onTransact(int code,Parcelable data,Parcelable reply,int flags)
  • 服務(wù)端通過code確定客戶端請求的目標(biāo)方法是什么,
    接著從data取出目標(biāo)方法所需的參數(shù),然后執(zhí)行目標(biāo)方法。
  • 執(zhí)行完畢后向reply寫入返回值( 如果有返回值)。
  • 如果這個方法返回值為false,那么服務(wù)端的請求會失敗,利用這個特性我們可以來做權(quán)限驗證。
  1. Proxy#getBookList 和Proxy#addBook
    這兩個方法運行在客戶端,內(nèi)部實現(xiàn)過程如下:
  • 首先創(chuàng)建該方法所需要的輸入型對象Parcel對象_data,輸出型Parcel對象_reply和返回值對象List。
  • 然后把該方法的參數(shù)信息寫入_data(如果有參數(shù))。
  • 接著調(diào)用transact方法發(fā)起RPC(遠(yuǎn)程過程調(diào)用),同時當(dāng)前線程掛起。
  • 然后服務(wù)端的onTransact方法會被調(diào)用知道RPC過程返回后,當(dāng)前線程繼續(xù)執(zhí)行,并從_reply中取出RPC過程的返回結(jié)果,最后返回_reply中的數(shù)據(jù)。

AIDL文件不是必須的,之所以提供AIDL文件,是為了方便系統(tǒng)為我們生成IBookManager.java,但我們完全可以自己寫一個。

linkToDeath和unlinkToDeath

如果服務(wù)端進(jìn)程異常終止,我們到服務(wù)端的Binder連接斷裂。但是,如果我們不知道Binder連接已經(jīng)斷裂,那么客戶端功能會受影響。通過linkTODeath我們可以給Binder設(shè)置一個死亡代理,當(dāng)Binder死亡時,我們就會收到通知。

  1. 聲明一個 DeathRecipient 對象。 DeathRecipient 是一個接口,只有一個方法 binderDied ,當(dāng)Binder死亡的時候,系統(tǒng)就會回調(diào) binderDied 方法,然后我們就可以重新綁定遠(yuǎn)程服務(wù)。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
    @Override
     public void binderDied(){
        if(mBookManager == null){
            return;
        } 
        mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
        mBookManager = null;
        // TODO:這里重新綁定遠(yuǎn)程Service
    }
};
  1. 在客戶端綁定遠(yuǎn)程服務(wù)成功后,給binder設(shè)置死亡代理:
mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient,0);
  1. 另外,可以通過Binder的 isBinderAlive 判斷Binder是否死亡。

Android中的IPC方式

主要有以下方式:

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

四大組件中的三大組件( Activity、Service、Receiver) 都支持在Intent中傳遞 Bundle 數(shù)據(jù)。
Bundle實現(xiàn)了Parcelable接口,因此可以方便的在不同進(jìn)程間傳輸。當(dāng)我們在一個進(jìn)程中啟動了另一個進(jìn)程的Activity、Service、Receiver,可以再Bundle中附加我們需要傳輸給遠(yuǎn)程進(jìn)程的消息并通過Intent發(fā)送出去。被傳輸?shù)臄?shù)據(jù)必須能夠被序列化。

使用文件共享

我們可以序列化一個對象到文件系統(tǒng)中的同時從另一個進(jìn)程中恢復(fù)這個對象。

  1. 通過 ObjectOutputStream / ObjectInputStream 序列化一個對象到文件中,或者在另一個進(jìn)程從文件中反序列這個對象。注意:反序列化得到的對象只是內(nèi)容上和序列化之前的對象一樣,本質(zhì)是兩個對象。
  2. 文件并發(fā)讀寫會導(dǎo)致讀出的對象可能不是最新的,并發(fā)寫的話那就更嚴(yán)重了 。所以文件共享方式適合對數(shù)據(jù)同步要求不高的進(jìn)程之間進(jìn)行通信,并且要妥善處理并發(fā)讀寫問題。
  3. SharedPreferences 底層實現(xiàn)采用XML文件來存儲鍵值對。系統(tǒng)對它的讀/寫有一定的緩存策略,即在內(nèi)存中會有一份 SharedPreferences 文件的緩存,因此在多進(jìn)程模式下,系統(tǒng)對它的讀/寫變得不可靠,面對高并發(fā)讀/寫時 SharedPreferences 有很大幾率丟失數(shù)據(jù),因此不建議在IPC中使用 SharedPreferences 。
使用Messenger

Messenger可以在不同進(jìn)程間傳遞Message對象。是一種輕量級的IPC方案,底層實現(xiàn)是AIDL。它對AIDL進(jìn)行了封裝,使得我們可以更簡便的進(jìn)行IPC。

Messenger的工作原理

具體使用時,分為服務(wù)端和客戶端:

  1. 服務(wù)端:創(chuàng)建一個Service來處理客戶端請求,同時創(chuàng)建一個Handler并通過它來創(chuàng)建一個
    Messenger,然后再Service的onBind中返回Messenger對象底層的Binder即可。
 private final Messenger mMessenger = new Messenger (new xxxHandler());
  1. 客戶端:綁定服務(wù)端的Sevice,利用服務(wù)端返回的IBinder對象來創(chuàng)建一個Messenger,通過這個Messenger就可以向服務(wù)端發(fā)送消息了,消息類型是 Message 。如果需要服務(wù)端響應(yīng),則需要創(chuàng)建一個Handler并通過它來創(chuàng)建一個Messenger( 和服務(wù)端一樣) ,并通過 Message 的 replyTo 參數(shù)傳遞給服務(wù)端。服務(wù)端通過Message的 replyTo 參數(shù)就可以回應(yīng)客戶端了。

總而言之,就是客戶端和服務(wù)端 拿到對方的Messenger來發(fā)送 Message 。只不過客戶端通過bindService 而服務(wù)端通過 message.replyTo 來獲得對方的Messenger。

Messenger中有一個 Hanlder 以串行的方式處理隊列中的消息。不存在并發(fā)執(zhí)行,因此我們不用考慮線程同步的問題。

使用AIDL

如果有大量的并發(fā)請求,使用Messenger就不太適合,同時如果需要跨進(jìn)程調(diào)用服務(wù)端的方法,Messenger就無法做到了。這時我們可以使用AIDL。

流程如下:

  1. 服務(wù)端需要創(chuàng)建Service來監(jiān)聽客戶端請求,然后創(chuàng)建一個AIDL文件,將暴露給客戶端的接口在AIDL文件中聲明,最后在Service中實現(xiàn)這個AIDL接口即可。
  2. 客戶端首先綁定服務(wù)端的Service,綁定成功后,將服務(wù)端返回的Binder對象轉(zhuǎn)成AIDL接口所屬的類型,接著就可以調(diào)用AIDL中的方法了。

AIDL支持的數(shù)據(jù)類型:

  • 基本數(shù)據(jù)類型、String、CharSequence
  • List:只支持ArrayList,里面的每個元素必須被AIDL支持
  • Map:只支持HashMap,里面的每個元素必須被AIDL支持
  • Parcelable
  • 所有的AIDL接口本身也可以在AIDL文件中使用

自定義的Parcelable對象和AIDL對象,不管它們與當(dāng)前的AIDL文件是否位于同一個包,都必須顯式import進(jìn)來。

如果AIDL文件中使用了自定義的Parcelable對象,就必須新建一個和它同名的AIDL文件,并在其中聲明它為Parcelable類型。

package com.ryg.chapter_2.aidl;
parcelable Book;

AIDL接口中的參數(shù)除了基本類型以外都必須表明方向in/out。AIDL接口文件中只支持方法,不支持聲明靜態(tài)常量。建議把所有和AIDL相關(guān)的類和文件放在同一個包中,方便管理。

void addBook(in Book book);

AIDL方法是在服務(wù)端的Binder線程池中執(zhí)行的,因此當(dāng)多個客戶端同時連接時,管理數(shù)據(jù)的集合直接采用 CopyOnWriteArrayList 來進(jìn)行自動線程同步。類似的還有 ConcurrentHashMap 。

因為客戶端的listener和服務(wù)端的listener不是同一個對象,所以 RemoteCallbackList 是系統(tǒng)專門提供用于刪除跨進(jìn)程listener的接口,支持管理任意的AIDL接口,因為所有AIDL接口都繼承自 I 接口。

public class RemoteCallbackList<E extends IInterface>

它內(nèi)部通過一個Map接口來保存所有的AIDL回調(diào),這個Map的key是 IBinder 類型,value是 Callback 類型。當(dāng)客戶端解除注冊時,遍歷服務(wù)端所有l(wèi)istener,找到和客戶端listener具有相同Binder對象的服務(wù)端listenr并把它刪掉。

客戶端RPC的時候線程會被掛起,由于被調(diào)用的方法運行在服務(wù)端的Binder線程池中,可能很耗時,不能在主線程中去調(diào)用服務(wù)端的方法。

權(quán)限驗證
默認(rèn)情況下,我們的遠(yuǎn)程服務(wù)任何人都可以連接,我們必須加入權(quán)限驗證功能,權(quán)限驗證失敗則無法調(diào)用服務(wù)中的方法。通常有兩種驗證方法:

  • 在onBind中驗證,驗證不通過返回null。
    驗證方式比如permission驗證,在AndroidManifest聲明:
<permission
android:name="com.rgy.chapter_2.permisson.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal"/>

Android自定義權(quán)限和使用權(quán)限:

public IBinder onBind(Intent intent){
    int check = checkCallingOrSelefPermission("com.ryq.chapter_2.permission.ACCESS_BOOK_SERVICE");
    if(check == PackageManager.PERMISSION_DENIED){
        return null;
    }
    return mBinder;
}

這種方法也適用于Messager。

  • 在onTransact中驗證,驗證不通過返回false。
    可以permission驗證,還可以采用Uid和Pid驗證。
使用ContentProvider

ContentProvider是四大組件之一,天生就是用來進(jìn)程間通信。和Messenger一樣,其底層實現(xiàn)是用Binder。

系統(tǒng)預(yù)置了許多ContentProvider,比如通訊錄、日程表等。要RPC訪問這些信息,只需要通過ContentResolver的query、update、insert和delete方法即可。

創(chuàng)建自定義的ContentProvider,只需繼承ContentProvider類并實現(xiàn) onCreate 、 query 、 update 、 insert 、 getType 六個抽象方法即可。getType用來返回一個Uri請求所對應(yīng)的MIME類型,剩下四個方法對應(yīng)于CRUD操作。這六個方法都運行在ContentProvider進(jìn)程中,除了 onCreate 由系統(tǒng)回調(diào)并運行在主線程里,其他五個方法都由外界調(diào)用并運行在Binder線程池中。

ContentProvider是通過Uri來區(qū)分外界要訪問的數(shù)據(jù)集合,例如外界訪問ContentProvider中的表,我們需要為它們定義單獨的Uri和Uri_Code。根據(jù)Uri_Code,我們就知道要訪問哪個表了。

query、update、insert、delete四大方法存在多線程并發(fā)訪問,因此方法內(nèi)部要做好線程同步。若采用SQLite并且只有一個SQLiteDatabase,SQLiteDatabase內(nèi)部已經(jīng)做了同步處理。若是多個SQLiteDatabase或是采用List作為底層數(shù)據(jù)集,就必須做線程同步。

使用Socket

Socket也稱為“套接字”,分為流式套接字和用戶數(shù)據(jù)報套接字兩種,分別對應(yīng)于TCP和UDP協(xié)議。Socket可以實現(xiàn)計算機網(wǎng)絡(luò)中的兩個進(jìn)程間的通信,當(dāng)然也可以在本地實現(xiàn)進(jìn)程間的通信。我們以一個跨進(jìn)程的聊天程序來演示。

在遠(yuǎn)程Service建立一個TCP服務(wù),然后在主界面中連接TCP服務(wù)。服務(wù)端Service監(jiān)聽本地端口,客戶端連接指定的端口,建立連接成功后,拿到 Socket 對象就可以向服務(wù)端發(fā)送消息或者接受服務(wù)端發(fā)送的消息。

除了采用TCP套接字,也可以用UDP套接字。實際上socket不僅能實現(xiàn)進(jìn)程間的通信,還可以實現(xiàn)設(shè)備間的通信(只要設(shè)備之間的IP地址互相可見)。

Binder連接池

前面提到AIDL的流程是:首先創(chuàng)建一個service和AIDL接口,接著創(chuàng)建一個類繼承自AIDL接口中的Stub類并實現(xiàn)Stub中的抽象方法,客戶端在Service的onBind方法中拿到這個類的對象,然后綁定這個service,建立連接后就可以通過這個Stub對象進(jìn)行RPC。

那么如果項目龐大,有多個業(yè)務(wù)模塊都需要使用AIDL進(jìn)行IPC,隨著AIDL數(shù)量的增加,我們不能無限制地增加Service,我們需要把所有AIDL放在同一個Service中去管理。

Binder連接池的工作原理
  • 服務(wù)端只有一個Service,把所有AIDL放在一個Service中,不同業(yè)務(wù)模塊之間不能有耦合。
  • 服務(wù)端提供一個 queryBinder 接口,這個接口能夠根據(jù)業(yè)務(wù)模塊的特征來返回響應(yīng)的Binder對象給客戶端。
  • 不同的業(yè)務(wù)模塊拿到所需的Binder對象就可以進(jìn)行RPC了。

選用合適的IPC方式

IPC方式的優(yōu)點缺點和適用場景

更多內(nèi)容戳這里(整理好的各種文集)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容