Android中IPC機(jī)制的原理是什么

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

沁源網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,沁源網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為沁源成百上千提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)營(yíng)銷(xiāo)網(wǎng)站建設(shè)要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的沁源做網(wǎng)站的公司定做!

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

IPC的使用場(chǎng)景就必須提到多進(jìn)程,只有面對(duì)多進(jìn)程這種場(chǎng)景下,才需要考慮進(jìn)程間通訊。多進(jìn)程的情況分為兩種:***種是一個(gè)應(yīng)用因?yàn)槟承┰蜃陨硇枰捎枚噙M(jìn)程模式來(lái)實(shí)現(xiàn),原因有很多,應(yīng)用特殊原因需要運(yùn)行的單獨(dú)的進(jìn)程中,或者為了加大一個(gè)應(yīng)用可使用內(nèi)存所以需要通過(guò)多進(jìn)程來(lái)獲取多分內(nèi)存空間。另外一種情況是:當(dāng)前應(yīng)用需要向其他應(yīng)用獲取數(shù)據(jù),由于是兩個(gè)應(yīng)用,所以必須采取跨進(jìn)程方式來(lái)獲取所需要數(shù)據(jù)。

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

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

給process指定多進(jìn)程有兩種不同的形式

  • :remote

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

  • com.xxx.xxx

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

多進(jìn)程運(yùn)行機(jī)制

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

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

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

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

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

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

IPC基礎(chǔ)概念介紹

  • Serializable接口

Serializable是Java所提供的一個(gè)序列號(hào)接口,它是一個(gè)空接口,為對(duì)象提供標(biāo)準(zhǔn)的序列化和反序列化操作。使用Serializable相當(dāng)簡(jiǎn)單,只需要實(shí)現(xiàn)Serializable接口并聲明一個(gè)serialVersionUID,其實(shí)這個(gè)serialVersionUID也不是必需的,如果不聲明這個(gè)serialVersionUID也是可以實(shí)現(xiàn)序列化的,但是這將會(huì)對(duì)反序列化過(guò)程產(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是用來(lái)輔助序列化和反序列化過(guò)程的,原則上序列化后的數(shù)據(jù)中serialVersionUID只有和當(dāng)前類(lèi)serialVersionUID相同才能夠正常的被反序列化。serialVersionUID的詳細(xì)工作機(jī)制是這樣的:序列化的時(shí)候系統(tǒng)會(huì)把當(dāng)前類(lèi)的serialVersionUID寫(xiě)入序列化的文件中(也可能是其他中介),但反序列化的時(shí)候會(huì)去檢測(cè)文件中的serialVersionUID,看它是否和當(dāng)前類(lèi)的serialVersionUID一致,如果一致就說(shuō)明序列化的版本和當(dāng)前版本是相同的,這個(gè)時(shí)候可以成功的反序列化,否則就說(shuō)明當(dāng)前類(lèi)和序列化的類(lèi)相比發(fā)生了某些變換,比如成員變量的數(shù)量、類(lèi)型發(fā)生了改變,這個(gè)時(shí)候無(wú)法正常反序列化。

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

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

- Parceable 接口

Parceable也是一個(gè)接口,只有實(shí)現(xiàn)這個(gè)接口,一個(gè)類(lèi)的對(duì)象就可以實(shí)現(xiàn)序列化并可以通過(guò)Intent和Binder傳遞。

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

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

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

- Serializable是Java中的序列化接口,其使用起來(lái)簡(jiǎn)單但是開(kāi)銷(xiāo)大,序列化和反序列化過(guò)程都需要大量的 I/O操作。

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

- Binder

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

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

//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);       }上面三個(gè)文件中,Book.java是一個(gè)表示圖書(shū)信息的類(lèi),它實(shí)現(xiàn)了Parcelable接口。Book.aidl 是Book類(lèi)在AIDL中的聲明。IBookManager.aidl是我們定義的一個(gè)接口,里面有兩個(gè)方法 getBookList() 和addBook(),其中g(shù)etBookList用于從遠(yuǎn)程服務(wù)端獲取圖書(shū)列表,而addBook是添加一本書(shū)。雖然Book類(lèi)已經(jīng)和IBookManager位于相同的包中,但I(xiàn)BookManager仍然需要導(dǎo)入Book類(lèi),這就是AIDL的特殊之處。builde的項(xiàng)目,系統(tǒng)為我們?cè)趃en目錄下生產(chǎn)IBookManage.java的類(lèi),接下來(lái)我們需要根據(jù)這個(gè)系統(tǒng)生成的IBookManag類(lèi)來(lái)分析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這個(gè)類(lèi),它繼承了IInterface這個(gè)接口,同時(shí)它自己也還是個(gè)接口,所以可以在Binder中傳輸?shù)慕涌诙夹枰^承IInterface接口。

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

  • DESCRIPTOR

Binder的唯一標(biāo)識(shí),一般用于當(dāng)前的Binder的類(lèi)名表示,比如本例中的 “com.example.xiahao.IBookManger”

  • asInterface(android.os.IBinder obj)

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

  • asBinder

此方法用于返回當(dāng)前的Binder對(duì)象

  • onTransact

這個(gè)方法運(yùn)行在服務(wù)端的Binder線(xiàn)程池中,當(dāng)客戶(hù)端發(fā)起跨進(jìn)程請(qǐng)求的時(shí),遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層封裝后交由此方法來(lái)處理。該方法的原型為

@Override

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

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

  • Proxy#getBookList

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

Proxy#getBookList

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

Android中IPC機(jī)制的原理是什么

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

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

//銷(xiāo)毀代理類(lèi),重啟服務(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();               }           };

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

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

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

Android中的IPC方式

  • 使用Bundle

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

  • 文件共享

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

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

  • Messenger

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

  • 服務(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”

- 客戶(hù)端

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", "我是客戶(hù)端");                       message.setData(bundle);                         //當(dāng)客戶(hù)端發(fā)送消息的時(shí)候,需要把接受服務(wù)端回復(fù)的Messenger通過(guò)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中進(jìn)行數(shù)據(jù)傳遞必須將數(shù)據(jù)放入Message中,而Messenger和Message都是實(shí)現(xiàn)了Parcelable接口,因此可以跨進(jìn)程傳輸。實(shí)際上:通過(guò)Messenger來(lái)傳輸Message,Message中能使用的載體只有what、arg1、arg2、Bundle已經(jīng)replyTo。Message的另一個(gè)字段object在同一進(jìn)程中是很實(shí)用的,但是在進(jìn)程間通信時(shí)候,非系統(tǒng)的Parcelable對(duì)象無(wú)法通過(guò)object字段來(lái)傳輸。但可以實(shí)用Bundle,Bundle可以支持大量的數(shù)據(jù)類(lèi)型。

使用AIDL

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

大致步驟如下:

  • 服務(wù)端

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

  • 客戶(hù)端

首先綁定服務(wù)端的Service,綁定成功后,將服務(wù)端返回的Binder對(duì)象轉(zhuǎn)成AIDL接口所屬的類(lèi)型,接著就可以調(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文件中,并不是所有的類(lèi)型都支持的,支持的類(lèi)型如下:

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

- string 和CharSequence

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

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

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

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

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

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

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

- 使用ContentProvide

ContentProvide專(zhuān)門(mén)用來(lái)應(yīng)用之間的通訊,和Messenger一樣,ContentProvide底層也是Binder,雖然底層Binder但使用要比AIDL簡(jiǎn)單多,因?yàn)橄到y(tǒng)幫我們做了封裝。

- 使用Socket

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

- 選用合適的IPC方式

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

名稱(chēng) 優(yōu)點(diǎn) 缺點(diǎn) 適用場(chǎng)景

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

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

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

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

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

關(guān)于Android中IPC機(jī)制的原理是什么問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

分享標(biāo)題:Android中IPC機(jī)制的原理是什么
轉(zhuǎn)載來(lái)源:http://muchs.cn/article10/pdpego.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站云服務(wù)器、網(wǎng)站設(shè)計(jì)公司、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站維護(hù)動(dòng)態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

營(yíng)銷(xiāo)型網(wǎng)站建設(shè)