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ī)制的圖:
接下來(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)
猜你還喜歡下面的內(nèi)容