溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android中IPC機制的原理是什么

發(fā)布時間:2021-06-28 16:03:00 來源:億速云 閱讀:237 作者:Leah 欄目:移動開發(fā)

Android中IPC機制的原理是什么,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

IPC是 Inter-Proscess  Communication的縮寫,含義為進程間的通訊或者跨進程通訊,是指兩個進程之間進行數(shù)據(jù)交換的過程。按操作系統(tǒng)的中的描述,線程是CPU調(diào)度最小的單元,同時線程是一種有限的系統(tǒng)資源,而進程是指一個執(zhí)行單元,在PC和移動設(shè)備上指一個程序或者一個應(yīng)用。一個進程可以包含多個線程,因此進程和線程是包含于被包含的關(guān)系。

IPC的使用場景就必須提到多進程,只有面對多進程這種場景下,才需要考慮進程間通訊。多進程的情況分為兩種:***種是一個應(yīng)用因為某些原因自身需要采用多進程模式來實現(xiàn),原因有很多,應(yīng)用特殊原因需要運行的單獨的進程中,或者為了加大一個應(yīng)用可使用內(nèi)存所以需要通過多進程來獲取多分內(nèi)存空間。另外一種情況是:當前應(yīng)用需要向其他應(yīng)用獲取數(shù)據(jù),由于是兩個應(yīng)用,所以必須采取跨進程方式來獲取所需要數(shù)據(jù)。

Android中的多進程模式

開啟Android多進程模式很簡單,就是給四大組件(Activity,Service,Receiver,ContentProvider)在AndroidMenifest中指定android:process屬性。另外還有一種非常規(guī)的做法,那就是通過JNI在native層去fork一個新的進程。

給process指定多進程有兩種不同的形式

  • :remote

進程名以 “:”的含義是指要在進程名前面附加上當前的包名,這個進程屬于當前應(yīng)用的私有進程,其他應(yīng)用不可以和他跑在同一個進程。

  • com.xxx.xxx

這種屬于全局進程,其他應(yīng)用可以通過ShareUID方式可以和它跑在同一個進程,我們都知道系統(tǒng)會為每個應(yīng)用分配一個唯一的UID,具有相同UID的應(yīng)用才能共享數(shù)據(jù)。兩個應(yīng)用通過ShareUID跑在同一個進程,是需要相同的ShareUID并且簽名相同才可以。不管它們是不是跑在同一個進程中,具有相同ShareUID的它們可以訪問對方的私有數(shù)據(jù),如:data目錄、組件信息等。當然如果是在同一個進程中,除了data目錄、組件信息還能共享內(nèi)存數(shù)據(jù)。

多進程運行機制

我們知道Android為每一個應(yīng)用分配了一個獨立的虛擬機,或者說為每一個進程都分配了一個獨立的虛擬機,不同的虛擬機在內(nèi)存分配上有不同的地址空間,這就導(dǎo)致在不同的虛擬機訪問同一個類的對象會產(chǎn)生多分副本。

所有運行在不同進程中的四大組件,只要它們之間需要通過內(nèi)存來共享數(shù)據(jù),都會共享失敗,這也是多進程所帶來的主要影響,一般來說,使用多進程會造成如下幾方面的問題。

  • 靜態(tài)成員和單例模式完全失效(都是不同的虛擬機)

  • 線程同步機制完全失效(都不是同一塊內(nèi)存了)

  • SharePreferences 的可靠性下降(底層通過XML去執(zhí)行操作的,并發(fā)很可能出問題,甚至并發(fā)讀、寫都有可能出問題)

  • Application會多次創(chuàng)建(當一個組件跑在一個新的進程中,由于系統(tǒng)要在創(chuàng)建新的進程同時分配獨立的虛擬機,所以這個過程其實就是啟動一個應(yīng)用的過程,因此系統(tǒng)又把這個應(yīng)用重新啟動了一遍,既然都重新啟動了,那么自然會創(chuàng)建新的Application)

IPC基礎(chǔ)概念介紹

  • Serializable接口

Serializable是Java所提供的一個序列號接口,它是一個空接口,為對象提供標準的序列化和反序列化操作。使用Serializable相當簡單,只需要實現(xiàn)Serializable接口并聲明一個serialVersionUID,其實這個serialVersionUID也不是必需的,如果不聲明這個serialVersionUID也是可以實現(xiàn)序列化的,但是這將會對反序列化過程產(chǎn)生影響。

//序列化  r user = new User("xia","123455");           ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));           out.write(user);           out.close();           //反序列化           ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));           User newUser = (User)in.readObject();           in.close();

serialVersionUID是用來輔助序列化和反序列化過程的,原則上序列化后的數(shù)據(jù)中serialVersionUID只有和當前類serialVersionUID相同才能夠正常的被反序列化。serialVersionUID的詳細工作機制是這樣的:序列化的時候系統(tǒng)會把當前類的serialVersionUID寫入序列化的文件中(也可能是其他中介),但反序列化的時候會去檢測文件中的serialVersionUID,看它是否和當前類的serialVersionUID一致,如果一致就說明序列化的版本和當前版本是相同的,這個時候可以成功的反序列化,否則就說明當前類和序列化的類相比發(fā)生了某些變換,比如成員變量的數(shù)量、類型發(fā)生了改變,這個時候無法正常反序列化。

一般來說,我們應(yīng)該手動指定serialVersionUID的值,如1L,也可以根據(jù)自身結(jié)構(gòu)自動去生成它的hash值,這樣序列化和反序列化時兩者的serialVersionUID是相同的。如果不指定serialVersionUID的值,反序列化時當前類有所改變,比如增加或者刪除了某些成員變量,那么系統(tǒng)就會重新計算當前類型的hash值并把它賦值給serialVersionUID,這個時候當前類的serialVersionUID就和序列化數(shù)據(jù)中的serialVersionUID不一致,于是反序列化失敗,程序就會出現(xiàn)crash。所以避免反序列化過程的失敗。比如當版本升級后,我們很可能刪除了某個成員變量也可能增加了一些新的成員變量,這個時候序列化過程仍然能夠成功,程序可以***限度地恢復(fù)數(shù)據(jù),相反,如果不指定serialVersionUID的話,程序則會掛掉。當然我們還要考慮另外一種情況,如果類的結(jié)構(gòu)發(fā)生了非常規(guī)性的改變,比如修改了類名,修改了成員變量的類型,這個時候盡管serialVersionUID驗證通過,但是反序列化還是會失敗,因為類結(jié)構(gòu)有了毀滅性的改變,根本無法從老版本的數(shù)據(jù)中還原出一個新的類結(jié)構(gòu)對象。

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

- Parceable 接口

Parceable也是一個接口,只有實現(xiàn)這個接口,一個類的對象就可以實現(xiàn)序列化并可以通過Intent和Binder傳遞。

public class User implements Parcelable {            public int UserId;           public String userName;           public boolean isMale;            protected User(Parcel in) {                 //從序列化后的對象中創(chuàng)建原始對象               UserId = in.readInt();               userName = in.readString();               isMale = in.readByte() != 0;           }            public static final Creator<User> CREATOR = new Creator<User>() {               @Override               public User createFromParcel(Parcel in) {                     //從序列化后的對象中創(chuàng)建原始對象                   return new User(in);               }                @Override               public User[] newArray(int size) {                     //創(chuàng)建指定長度的原始對象數(shù)組                   return new User[size];               }           };            @Override           public int describeContents() {             /**                 返回當前對象的內(nèi)容描述。如果含有文件描述符,返回1,否則返回0,幾乎所有情況都返回0             */               return 0;           }            @Override           public void writeToParcel(Parcel dest, int flags) {                 /**將當前對象寫入序列化結(jié)構(gòu)中,其中flags,標識有0或1                  為1時標識當前對象需要作返回值返回,不能立即釋放資源,幾乎所有情況          都為0**/               dest.writeInt(UserId);               dest.writeString(userName);               dest.writeByte((byte) (isMale ? 1 : 0));           }       }

Parcel內(nèi)部包裝了可序列化的數(shù)據(jù),可以在Binder中自由傳輸,從上述代碼中可以看出,在序列化過程中需要實現(xiàn)的功能有序列化、反序列化和內(nèi)部描述序列化功能由writeParcel方法完成,最終是通過Parcel中的一系列write方法來完成的。反序列化功能由CREATOR來完成,其內(nèi)部標明了如何創(chuàng)建序列化對象和數(shù)組,并通過Parcel一系列read方法來完成反序列化過程;內(nèi)容描述功能由describeContents來完成,幾乎所有情況下這個方法都應(yīng)該返回0,僅當當前對象中存在文件描述符時,此方法返回1.系統(tǒng)已經(jīng)提供了許多實現(xiàn)Parcelable接口的類,它們都是可以直接序列化的,如:Intent、Bundle、Bitmap等,同時List  和 Map也可以序列化,前提時它們里面每個元素都是可序列化的。

既然Parcelable 和Serializable 都可以用于Intent間的數(shù)據(jù)傳遞,那么如何選擇了。

- Serializable是Java中的序列化接口,其使用起來簡單但是開銷大,序列化和反序列化過程都需要大量的 I/O操作。

-  Parcelable是Android中的序列化方式,更適用于在Android平臺上,它的缺點就是用起來稍微麻煩,但效率很高,這是Android推薦方式,因此,***Parcelable。但通過Parcelable將對象序列化到存儲設(shè)備中或?qū)ο笮蛄谢笸ㄟ^網(wǎng)絡(luò)傳輸也都是可以的,但是這個過程會稍顯復(fù)雜,因此這種情況下建議使用Serializable。

- Binder

Binder是一個非常復(fù)雜,這里只是介紹下Binder的使用及上層實現(xiàn)原理。

Binder是Android中的一個類,它實現(xiàn)了IBinder的接口。從IPC角度來說,Binder是Android中一種跨進程的通訊方式,Binder還可以理解為一種虛擬物理設(shè)備,它的設(shè)備驅(qū)動是  /dev/binder,該通訊方式在Linux中沒有;從Android  Framework,角度來說,Binder是ServiceManger連接各種Manger(ActivityManger  、WindowManger,等等)和相應(yīng)的MangeSrervice的橋梁;從Android應(yīng)用層來說,Binder是客戶端和服務(wù)端進行通訊的媒介,當bindSrervice的時候,服務(wù)端會返回一個包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對象,通過這個Binder對象,客戶端就可以用獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù),這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)。普通Srervice中的Binder不涉及進程間通信,下面通過AIDL來分析Binder的工作過程。

//Book.java     public class Book implements Parcelable{           int id;           String type;            public Book(int id, String type) {               this.id = id;               this.type = type;           }            @Override           public String toString() {               return "Book{" +                       "id=" + id +                       ", type='" + type + '\'' +                       '}';           }        @Override       public int describeContents() {           return 0;       }        @Override       public void writeToParcel(Parcel dest, int flags) {           dest.writeInt(this.id);           dest.writeString(this.type);       }        protected Book(Parcel in) {           this.id = in.readInt();           this.type = in.readString();       }        public static final Creator<Book> CREATOR = new Creator<Book>() {           @Override           public Book createFromParcel(Parcel source) {               return new Book(source);           }            @Override           public Book[] newArray(int size) {               return new Book[size];           }       };    }        ```java       // Book.aidl       package com.example.xiahao.myapplication;       parcelable Book;        // IBookManager.aidl       package com.example.xiahao.myapplication;        // Declare any non-default types here with import statements       import com.example.xiahao.myapplication.Book;        interface IBookManager {            List<Book> getBookList();              void addBook(in Book book);       }上面三個文件中,Book.java是一個表示圖書信息的類,它實現(xiàn)了Parcelable接口。Book.aidl 是Book類在AIDL中的聲明。IBookManager.aidl是我們定義的一個接口,里面有兩個方法 getBookList() 和addBook(),其中g(shù)etBookList用于從遠程服務(wù)端獲取圖書列表,而addBook是添加一本書。雖然Book類已經(jīng)和IBookManager位于相同的包中,但IBookManager仍然需要導(dǎo)入Book類,這就是AIDL的特殊之處。builde的項目,系統(tǒng)為我們在gen目錄下生產(chǎn)IBookManage.java的類,接下來我們需要根據(jù)這個系統(tǒng)生成的IBookManag類來分析Binder的工作原理  /*        * This file is auto-generated.  DO NOT MODIFY.        * Original file: /Users/xiahao/Documents/WorkSpace/AndroidStudioProjects/MyApplication/app/src/main/aidl/com/example/xiahao/myapplication/IBookManager.aidl        */       }

可以看到根據(jù)IBookManager.aidl系統(tǒng)為我們生成了IBookManager.java這個類,它繼承了IInterface這個接口,同時它自己也還是個接口,所以可以在Binder中傳輸?shù)慕涌诙夹枰^承IInterface接口。

首先,它聲明了兩個方法getBookList 和 addBook  ,這就是我們在IBookManger.aidl中所聲明的方法,同時它還聲明了兩個整數(shù)的id分別用于標識這兩個方法,這兩個id用標識在transact過程客戶端請求的到底是哪個方法。接著,還聲明了一個內(nèi)部類Stub,這個Stub就是一個Biner類,當客戶端和服務(wù)端都位于同一個進程中,方法調(diào)用不會走跨進程的transact過程,而當兩者位于不同的進程中,方法需要走transact過程,這個邏輯由Stub的內(nèi)部代理類  Proxy來完成。所以這個接口的實現(xiàn)核心就是它的內(nèi)部類Stud和Stub的內(nèi)部代理類 Proxy。

  • DESCRIPTOR

Binder的唯一標識,一般用于當前的Binder的類名表示,比如本例中的 “com.example.xiahao.IBookManger”

  • asInterface(android.os.IBinder obj)

用于將服務(wù)端的Binder對象轉(zhuǎn)換成客戶端所需要的AIDL接口的類型對象,這種轉(zhuǎn)換時區(qū)分進程的,如果客戶端和服務(wù)端位于同一進程,那么此方法返回的就是服務(wù)端的Stub對象本身,否則返回的時系統(tǒng)封裝后的Stub.proxy對象。

  • asBinder

此方法用于返回當前的Binder對象

  • onTransact

這個方法運行在服務(wù)端的Binder線程池中,當客戶端發(fā)起跨進程請求的時,遠程請求會通過系統(tǒng)底層封裝后交由此方法來處理。該方法的原型為

@Override

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel  reply, int flags)

服務(wù)端通過code可以確定客戶端請求的目標方法是什么,接著從data中取出目標方法所需要的參數(shù)(如果目標方法有參數(shù)的話),然后執(zhí)行目標方法,當目標方法執(zhí)行完畢后,就向reply中寫入返回值(如果目標方法有返回值的話),onTransact方法的執(zhí)行過程就是這樣的。需要注意的時,如果此方法返回false,那么客戶端的請求就會失敗,因此我們可以利用這個特性來做權(quán)限驗證,畢竟我們也不希望隨便一個進程都能遠程調(diào)用我們的服務(wù)。

  • Proxy#getBookList

這個方法運行在客戶端,當客戶端調(diào)用此方法時,它的內(nèi)部實現(xiàn)是這樣的:首先創(chuàng)建該方法所需要的的輸入型Prcel對象 _data、輸出型Prcel對象  _reply和返回值對象List;然后把該方法的參數(shù)信息寫入  _data中(如果有參數(shù)的話);接著調(diào)用transact方法來發(fā)起RPC(遠程過程調(diào)用)請求,同時當前線程掛起;然后服務(wù)端onTransact方法會被調(diào)用,直到RPC過程返回后,當前線程繼續(xù)執(zhí)行,并  _reply中取出RPC過程的返回結(jié)果;***返回 _reply中的數(shù)據(jù)。

Proxy#getBookList

這個方法運行在客戶端,它的執(zhí)行過程和getBookList是一樣的,addBook沒有返回值,所以他不需要從  _reply中取出返回值。注意:當客戶端發(fā)起遠程請求時,由于當前線程會被掛起直至服務(wù)器返回數(shù)據(jù),所以如果一個遠程的方法是很耗時的話,那么不能再UI線程中發(fā)起次遠程請求;其次,由于服務(wù)端的Binder方法運行在Binder的線程池中,所以Binder方法不管是否耗時都應(yīng)該采用同步的方式實現(xiàn),因為它已經(jīng)運行在一個線程中了。為了更好的說明Binder,下面給出一個工作機制的圖:

Android中IPC機制的原理是什么

接下來,介紹下Binder的兩個很重要的方法 linkTodeath 和  unlinkTodeath,如果服務(wù)端的Binder連接斷裂 (稱之為 Binder  死亡),會導(dǎo)致我們遠程調(diào)用失敗。更為關(guān)鍵的時,如果我們不知道Binder的連接已經(jīng)斷裂,那么客戶端的功能就會受到影響。為此我們可以給Binder設(shè)置一個死亡代理,當Binder死亡時,我們就會收到通知,這個時候我們就可以給Binder設(shè)置一個死亡代理,這個時候就可以重新發(fā)起連接請求從而恢復(fù)連接。

聲明一個IBinder.DeathRecipient對象,IBinder.DeathRecipient是一個接口,其內(nèi)部只有一個binderDied,我們需要實現(xiàn)這個方法,當binder死亡的時候,系統(tǒng)就會回調(diào)binderDied方法,然后我們就可以移除之前綁定的binder代理并重新綁定遠程服務(wù):

//銷毀代理類,重啟服務(wù)  private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {               @Override               public void binderDied() {                   Log.w(TAG, "binder: deed");                   mIBinderPool.asBinder().unlinkToDeath(mDeathRecipient, 0);                   mIBinderPool = null;                   connectBinderPoolService();               }           };

在客戶端綁定遠程服務(wù)成功后,給binder設(shè)置死亡代理

mIBinderPool = IBinderPool.Stub.asInterface(iBinder);                   try {                       mIBinderPool.asBinder().linkToDeath(mDeathRecipient,0);                   } catch (RemoteException e) {                       e.printStackTrace();                   }

其中l(wèi)inkDeath的第二個參數(shù)是個標記位,我們直接設(shè)為0即可。經(jīng)過上面的兩個步驟就給我們的binder設(shè)置了死亡代理,當binder死亡的時候我們就可以收到通知了。另外Binder的方法isBinderAlive也可以判斷Binder是否死亡。

Android中的IPC方式

  • 使用Bundle

四大組件中的三大組件(Activity、Service、Receiver)都是支持Intent中傳遞Bundle數(shù)據(jù)的,由于Bundle實現(xiàn)了Pracel接口,所以它可以很方便的在不同的進程間傳輸?;谶@一點,當我們調(diào)用了另一個進程中的Activity、Service、Receiver時,我們就可以在Bundle中附加我們需要傳輸給遠程進程的信息并通過Intent發(fā)生出去。當然,被傳輸?shù)臄?shù)據(jù)必須能夠被序列化,比如基本類型,實現(xiàn)了Pracelable、Serialzable接口以及一些Android支持的特殊對象。具體可以看Bundle這個類!

  • 文件共享

兩個進程通過讀/寫同一個文件交換數(shù)據(jù),比如A進程把數(shù)據(jù)寫入文件,B進程通過讀取這個文件來獲取數(shù)據(jù)。由于Android系統(tǒng)基于Linux,所以并發(fā)讀/寫文件沒有限制性,甚至兩個線程對同一個文件進行寫操作都是允許的,盡管這會出現(xiàn)問題,除了交換基本信息之外,我們可以序列化一個對象到文件系統(tǒng)中的同時從另一個進程恢復(fù)這個對象。文件共享的局限性是,并發(fā)讀/寫問題,如果并發(fā)讀/寫,讀出的數(shù)據(jù)可能不是***的,如果是并發(fā)寫的話就更嚴重了。

SharePreferences也屬于文件的一種,但是由于系統(tǒng)對它的讀/寫有一定的緩存策略,即在內(nèi)存中會有一份SharePreferences文件的緩存,因此在多進程的情況下,系統(tǒng)對它的讀/寫變得不可靠,當面對高并發(fā)讀/寫數(shù)據(jù)就很有很大幾率丟失數(shù)據(jù),不建議在進程間通信中使用SharePreferences。

  • Messenger

通過Messenger可以在不同進程中傳遞Message對象,在Message中放入我們需要傳入的數(shù)據(jù),就可以實現(xiàn)數(shù)據(jù)的進程間傳遞了。Messenger是一種輕量級的IPC方案,它的底層實現(xiàn)是AIDL,并對AIDI做了封裝,使得可以很方便的進行進程間通信。同時,由于處理一個請求,因此在服務(wù)端我們不用考慮線程同步的問題。

  • 服務(wù)端相關(guān)代碼 

private static final String TAG = "MessengerService";               private static class MessengerServiceHandler extends Handler {                  @Override                  public void handleMessage(Message msg) {                      switch (msg.what) {                          case Constants.MSG_FROM_CLIENT:                              Log.i(TAG, "server form client\t" + msg.getData().getString("msg"));                              Messenger client = msg.replyTo;                              Message replyMessage = Message.obtain(null, Constants.MSG_FROM_SERVICE);                              Bundle bundle = new Bundle();                              bundle.putString("reply", "收到消息,我是服務(wù)端!");                              replyMessage.setData(bundle);                              try {                                  client.send(replyMessage);                              } catch (RemoteException e) {                                  e.printStackTrace();                              }                              break;                          default:                              super.handleMessage(msg);                      }                  }              }               private final Messenger mMessenger = new Messenger(new MessengerServiceHandler());               @Override              public IBinder onBind(Intent intent) {                  return mMessenger.getBinder();              }

在AndroidManifest 配置服務(wù) android:process=”:remote”

- 客戶端

private Messenger mMessenger;               private static final String TAG = "MainActivity";                @Override               protected void onCreate(Bundle savedInstanceState) {                   super.onCreate(savedInstanceState);                   setContentView(R.layout.activity_main);                   Intent intent = new Intent(this, MessengerService.class);                   bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);               }                private ServiceConnection mServiceConnection = new ServiceConnection() {                   @Override                   public void onServiceConnected(ComponentName name, IBinder service) {                       mMessenger = new Messenger(service);                       Message message = Message.obtain(null, Constants.MSG_FROM_CLIENT);                       Bundle bundle = new Bundle();                       bundle.putString("msg", "我是客戶端");                       message.setData(bundle);                         //當客戶端發(fā)送消息的時候,需要把接受服務(wù)端回復(fù)的Messenger通過Message的replytTo參數(shù)傳遞給服務(wù)端。                       message.replyTo = mGetReplyMessenger;                       try {                           mMessenger.send(message);                       } catch (RemoteException e) {                           e.printStackTrace();                       }                   }                    @Override                   public void onServiceDisconnected(ComponentName name) {                    }               };            private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());            private static class MessengerHandler extends Handler {               @Override               public void handleMessage(Message msg) {                   switch (msg.what) {                       case Constants.MSG_FROM_SERVICE:                           Log.i(TAG, "receive msg from Service\t" + msg.getData().getString("reply"));                           break;                       default:                           super.handleMessage(msg);                   }               }           }            @Override           protected void onDestroy() {               super.onDestroy();               unbindService(mServiceConnection);           }       ```

在Messenger中進行數(shù)據(jù)傳遞必須將數(shù)據(jù)放入Message中,而Messenger和Message都是實現(xiàn)了Parcelable接口,因此可以跨進程傳輸。實際上:通過Messenger來傳輸Message,Message中能使用的載體只有what、arg1、arg2、Bundle已經(jīng)replyTo。Message的另一個字段object在同一進程中是很實用的,但是在進程間通信時候,非系統(tǒng)的Parcelable對象無法通過object字段來傳輸。但可以實用Bundle,Bundle可以支持大量的數(shù)據(jù)類型。

使用AIDL

用Messenger來進行進程間通信時是以串行的方式處理客戶端發(fā)來的消息,如果大量的消息同時發(fā)送到服務(wù)端,服務(wù)端仍然只能一個個處理,如果大量的并發(fā)請求,那么用Messenger就不太合適了。同時Messenger的作用主要是為了傳遞消息,很多時候我們可能需要跨進程調(diào)用服務(wù)端方法,這個時候我們就可以用AIDL了。

大致步驟如下:

  • 服務(wù)端

服務(wù)端首先要創(chuàng)建一個Service用來監(jiān)聽客戶端的連接,然后創(chuàng)建一個AIDL文件,將暴露給客戶端的接口在這個AIDL文件中聲明,***在Service中實現(xiàn)這個AIDL文件即可。

  • 客戶端

首先綁定服務(wù)端的Service,綁定成功后,將服務(wù)端返回的Binder對象轉(zhuǎn)成AIDL接口所屬的類型,接著就可以調(diào)用AIDL中的方法。

  • AIDL接口的創(chuàng)建

// IBookManager.aidl  com.example.xiahao.myapplication;  // Declare any non-default types here with import statements import com.example.xiahao.myapplication.Book; import com.example.xiahao.myapplication.IOnNewBookArrivedListener;  interface IBookManager {       List<Book> getBookList();      void addBook(in Book book); }

在AIDL文件中,并不是所有的類型都支持的,支持的類型如下:

- 基本數(shù)據(jù)類型(int 、long 、char、 boolean 、double等);

- string 和CharSequence

- List 只支持ArrayList,里面每個元素必須能夠被AIDL支持

- Map 只支持HashMap ,里面每個元素必須能夠被AIDL支持,包括key 和value

- Parcelable:所有實現(xiàn)了Parcelable接口的對象

- AIDL:所有AIDL接口本身也可以在AIDL文件中使用。

以上6中類型,其中自定義Parcelable對象和AIDL文件必須顯示的import進來,不管是否和當前的AIDL位于同一文件中。另外,如果AIDL用到了自定義的Parcelable對象必須新建一個和它同名的的AIDL文件,上面我們用到了Book,所以必須創(chuàng)建Book.aidl.

// Book.aidl  package com.example.xiahao.myapplication;  parcelable Book;

為了方便開發(fā),建議把所以AIDL相關(guān)類和文件全部放入同一包中,當客戶端是另外一個應(yīng)用時,我們可以直接把整個包復(fù)制到客戶端工程中。后面會給出一個書上的例子:具體包含,基本的AIDL調(diào)用,注冊解注冊,權(quán)限驗證,斷開重連,binder連接池一個服務(wù)處理多個AIDL的調(diào)用。

- 使用ContentProvide

ContentProvide專門用來應(yīng)用之間的通訊,和Messenger一樣,ContentProvide底層也是Binder,雖然底層Binder但使用要比AIDL簡單多,因為系統(tǒng)幫我們做了封裝。

- 使用Socket

通過Socket進行進程間的通訊,它分為流式套接字和用戶數(shù)據(jù)套接字兩種,分別對應(yīng)網(wǎng)絡(luò)協(xié)議層中的TCP和UDP協(xié)議。TCP是面向連接的協(xié)議,提供穩(wěn)定的雙向的通訊功能,TCP的建立需要經(jīng)過  “三次握手”才能完成,為提供穩(wěn)定的的數(shù)據(jù)傳輸功能,其本身提供了超時重連機制,因此具有很高的穩(wěn)定性。而UDP是無連接的,提供不穩(wěn)定的單向的通訊功能,當然UDP也可以實現(xiàn)雙向通訊功能。在性能上,UDP具有更好的效率,其缺點就是不保證數(shù)據(jù)一定能夠正確傳輸,尤其是在網(wǎng)絡(luò)擁塞的情況下。

- 選用合適的IPC方式

給出書中的一張表格《Android開發(fā)藝術(shù)探討》

名稱 優(yōu)點 缺點 適用場景

Bundle 簡單易用 只能傳輸Bundle支持的數(shù)據(jù)類型 四大組件的進程間通信

文件共享 簡單易用 不適合高并發(fā)場景,并且無法做到進程間的即時通訊 無并發(fā)訪問情形,交換簡單的數(shù)據(jù)實時性不高的場景

AIDL 功能強大,支持一對多并發(fā)通信,支持實時通信 使用稍復(fù)雜,需要處理好線程同步 一對多通信且RPC需要Messenger  功能一般,支持一對多串行通信,支持實時通信 不能很好的處理高并發(fā)情形,不支持RPC,數(shù)據(jù)通過Message進行傳輸,因此只能傳輸Bundle支持的數(shù)據(jù)類型  低并發(fā)的一對多即時通訊,無RPC需要,或者無需返回結(jié)果的RPC需求

ContentProvider 在數(shù)據(jù)訪問功能很強大,支持一對多并發(fā)數(shù)據(jù)共享,可通過call方法擴展其他操作  可以理解為受約束的AIDL,主要提供數(shù)據(jù)的CRUD操作 一對多的進程間的數(shù)據(jù)共享

Socket 功能強大,可以通過網(wǎng)絡(luò)傳輸字節(jié)流,并支持一對多并發(fā)實時通信 實現(xiàn)細節(jié)稍微有點繁瑣,不支持直接的RPC 網(wǎng)絡(luò)數(shù)據(jù)交換

關(guān)于Android中IPC機制的原理是什么問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI