(1) IPC是Inter-Process-Communication的縮寫,含義為進程間通信或跨進程通信挑童,是指兩個進程之間進行數(shù)據(jù)交換的過程观挎。
(2) 進程和線程的區(qū)別
a.按照操作系統(tǒng)的描述态秧,線程是CPU調(diào)度的最小單元昂验,同時線程是一種有限的系統(tǒng)資源缘滥。
b.進程一般指一個執(zhí)行單元券躁,在PC和移動設(shè)備上指一個程序或者一個應(yīng)用惩坑。一個進程可以包含多個線程,因此進程和線程是包含
與被包含的關(guān)系也拜。
(3) 多進程分為兩種
a.第一種情況是一個應(yīng)用因為某些原因自身需要采用多線程模式來實現(xiàn)以舒。在android 中實現(xiàn)多進程只有一種方法,就是在注冊四大組件的時候給其指定process屬性.進程名以":"開頭的進程屬于當(dāng)前應(yīng)用的私有進程,其他應(yīng)用的組件不可以和它跑在同一進程慢哈,而進程名不以":"開頭的進程屬于全局進程蔓钟,其他應(yīng)用通過ShareUID方式可以和它跑在同一個進程中。ndroid系統(tǒng)會為每個應(yīng)用分配一個唯一的UID,具有相同UID的應(yīng)用才能共享數(shù)據(jù)卵贱,兩個應(yīng)用通過ShareUID跑在同一個進程中是有要求的滥沫,需要這兩個應(yīng)用有相同的ShareUID并且簽名相同才可以。在這種情況下键俱,它們可以互相訪問對方的私有數(shù)據(jù)兰绣,比如data目錄、組件信息等编振,不管它們是否跑在同一個進程中缀辩。當(dāng)然如果它們跑在同一個進程中,那么除了能共享data目錄踪央、組件信息臀玄,還可以共享內(nèi)存數(shù)據(jù),或者說它們看起來就像是一個應(yīng)用的兩個部分畅蹂。
b.另一種情況是當(dāng)前應(yīng)用需要向其他應(yīng)用獲取數(shù)據(jù)
(4) 多進程模式的運行機制
Android為每一個應(yīng)用分配了一個獨立的虛擬機健无,或者說為每個進程都分配了一個獨立的虛擬機,不同的虛擬機在不同的內(nèi)存分配上有不同的地址空間液斜,這就導(dǎo)致在不同的虛擬機中訪問同一個類的對象會產(chǎn)生多份副本累贤。
所有運行在不同進程中的四大組件募胃,只要它們之間需要通過內(nèi)存來共享數(shù)據(jù),都會共享失敗畦浓。
一般來說,使用多進程會造成如下幾個方面的問題:
靜態(tài)成員和單例模式完全失效
線程同步機制完全失效
不管是鎖對象還是鎖全局類都無法保證線程同步检疫,因為不同進程鎖的不是同一個對象
SharedPreference的可靠性下降
SharedPreferences不支持兩個進程同時去執(zhí)行寫操作讶请,否則會導(dǎo)致一定幾率的數(shù)據(jù)丟失,這時因為SharedPreferences底層是通過讀寫XML文件來實現(xiàn)的屎媳,并發(fā)寫顯然是可能出問題的夺溢,甚至并發(fā)讀寫都有可能發(fā)生問題
Application會多次創(chuàng)建
運行在同一個進程中的組件是屬于同一個虛擬機和同一個Application的。同理烛谊,運行在不同進程中的組件是屬于兩個不同的虛擬機和Application的风响。
(5) IPC基礎(chǔ)概念介紹
Serializable
是Java所提供的一個序列化接口,它是一個空接口丹禀,為對象提供標準的序列化和反序列化操作。使用Serializable來實現(xiàn)序列化相當(dāng)簡單,只需要在類的聲明中指定一個類似下面的標識即可自動實現(xiàn)默認的序列化過程杂靶。
private static final long serialVersionUID = 8711368828010083044L
通過Serializable方來實現(xiàn)對象的序列化吉殃,如下代碼:
//序列化過程
User user = new User(0, "jake", true);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
//反序列化過程
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User newUser = (User)in.readObject();
in.close();
原則上序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類的serialVersionUID相同時才能夠正常的被反序列化。serialVersionUID的詳細工作機制是這樣的:序列化的時候系統(tǒng)會把當(dāng)前類的serialVersionUID寫入序列化的文件中(也可能是其他的中介)焙矛,當(dāng)反序列化的時候系統(tǒng)會去檢測文件中的serialVersionUID葫盼,看它是否和當(dāng)前類的serialVersionUID一致,如果一致就說明序列化的類的版本和當(dāng)前類的版本是相同的村斟,這個時候可以成功反序列化贫导,否則就說明當(dāng)前類和序列化的類相比發(fā)生了某些變換
給serialVersionUID制定為1L或者采用Eclipse根據(jù)當(dāng)前類結(jié)構(gòu)去生成的hash值,這兩者并沒有本質(zhì)區(qū)別蟆盹。
靜態(tài)成員變量屬于類不屬于對象孩灯,所以不會參與序列化過程
其次用transient關(guān)鍵字標記的成員變量不參與序列化過程
Parcelable接口
Parcelable也是一個接口,只要實現(xiàn)這個接口日缨,一個類的對象就可以實現(xiàn)序列化并可以通過Intent的Binder傳遞
系統(tǒng)已經(jīng)為我們提供了許多實現(xiàn)了Parcelable接口的類钱反,它們都是可以直接序列化的,比如Intent匣距、Bundle面哥、Bitmap等,同時List和Map也可以序列化毅待,前提是它們里面的每個元素都是可序列化的尚卫。
(6) 如何選取
Serializable是Java中的序列化接口,其使用起來簡單但是開銷很大尸红,序列化和反序列化需要大量I/O操作吱涉。而Parceleble是Android中的序列化方式刹泄,因此更適合在Android平臺上,缺點是麻煩怎爵,但是效率高特石,這是Android推薦的序列化方式,所以我們要首選Parcelable鳖链。Parcelable主要用在內(nèi)存序列化上姆蘸,通過Parcelable將對象序列化到存儲設(shè)備中或者將對象序列化之后通過網(wǎng)絡(luò)傳輸,但是過程稍顯復(fù)雜芙委,因此在這兩種情況下建議大家使用Serializable逞敷。
(7)AIDL android-Interface-define-language的縮寫
Binder
繼承了IBinder接口,Binder是一種跨進程通信方式是ServiceManager連接各種Manager(ActivityManager,WindowManager等)和相應(yīng)ManagerService的橋梁
從Android應(yīng)用層來說,Binder是客戶端和服務(wù)端進行通信的媒介灌侣,當(dāng)bindService的時候推捐,服務(wù)器會返回一個包含了服務(wù)器端業(yè)務(wù)調(diào)用的Binder對象,通過這個Binder對象侧啼,客戶端就可以獲取服務(wù)端提供的服務(wù)或者是數(shù)據(jù)牛柒,這里的服務(wù)包含了普通服務(wù)和基于AIDL的服務(wù)
aidl工作流程
aidl工具根據(jù)aidl文件自動生成的java接口的解析:首先,它聲明了幾個接口方法痊乾,同時還聲明了幾個整型的id用于標識這些方法焰络,id用于標識在transact過程中客戶端所請求的到底是哪個方法;接著符喝,它聲明了一個內(nèi)部類Stub闪彼,這個Stub就是一個Binder類,當(dāng)客戶端和服務(wù)端都位于同一個進程時协饲,方法調(diào)用不會走跨進程的transact過程畏腕,而當(dāng)兩者位于不同進程時,方法調(diào)用需要走transact過程茉稠,這個邏輯由Stub內(nèi)部的代理類Proxy來完成描馅。 所以,這個接口的核心就是它的內(nèi)部類Stub和Stub內(nèi)部的代理類Proxy而线。 下面分析其中的方法:
asInterface(android.os.IBinder obj):用于將服務(wù)器端的Binder對象轉(zhuǎn)化成客戶端所需的AIDL接口類型的對象铭污,這種轉(zhuǎn)換過程是區(qū)分進程的,如果客戶端和服務(wù)端是在同一進程中膀篮,那么這個方法返回的是服務(wù)端的Stub對象本身嘹狞,否則返回的是系統(tǒng)封裝的Stub.Proxy對象。
asBinder:返回當(dāng)前Binder對象
onTransact:這個方法運行在服務(wù)端中的Binder線程池中誓竿,當(dāng)客戶端發(fā)起跨進程請求時磅网,遠程請求會通過系統(tǒng)底層封裝后交由此方法來處理。 這個方法的原型是public Boolean onTransact(int code, Parcelable data, Parcelable reply, int flags) 服務(wù)端通過code可以知道客戶端請求的目標方法筷屡,接著從data中取出所需的參數(shù)涧偷,然后執(zhí)行目標方法簸喂,執(zhí)行完畢之后,將結(jié)果寫入到reply中燎潮。如果此方法返回false喻鳄,說明客戶端的請求失敗,利用這個特性可以做權(quán)限驗證(即驗證是否有權(quán)限調(diào)用該服務(wù))确封。
Proxy#[Method]:代理類中的接口方法诽表,這些方法運行在客戶端,當(dāng)客戶端遠程調(diào)用此方法時隅肥,它的內(nèi)部實現(xiàn)是:首先創(chuàng)建該方法所需要的參數(shù),然后把方法的參數(shù)信息寫入到_data中袄简,接著調(diào)用transact方法來發(fā)起RPC請求腥放,同時當(dāng)前線程掛起;然后服務(wù)端的onTransact方法會被調(diào)用绿语,直到RPC過程返回后秃症,當(dāng)前線程繼續(xù)執(zhí)行,并從_reply中取出RPC過程的返回結(jié)果吕粹,最后返回_reply中的數(shù)據(jù)种柑。
另外,當(dāng)客戶端發(fā)起遠程請求時匹耕,由于當(dāng)前線程會被掛起直至服務(wù)端進程返回數(shù)據(jù)聚请,所以如果一個遠程方法是很耗時的,那么不能在UI線程發(fā)起此遠程請求稳其;其次驶赏,由于服務(wù)端的Binder方法運行在Binder的線程池中,所以不管Binder是否耗時都應(yīng)該采用同步的方式去實現(xiàn)既鞠,因為它已經(jīng)運行在一個線程中了煤傍。
Binder兩種重要的方法
linkToDeath
unlinkToDeath Binder運行在服務(wù)端,如果由于某種服務(wù)端異常終止了的話會導(dǎo)致客戶端的遠程調(diào)用失敗嘱蛋、所以Binder提供了兩個配對的方法linkToDeath和unlinkToDeath蚯姆,通過linkToDeath方法可以給Binder設(shè)置一個死亡代理,當(dāng)Binder死亡的時候客戶端就會收到通知洒敏,然后就可以重新發(fā)起連接從而恢復(fù)連接了龄恋。 ###如何給Binder設(shè)置死亡代理 1、聲明一個DeathRecipient對象凶伙、DeathRecipient是一個接口篙挽,其內(nèi)部只有一個方法bindDied,實現(xiàn)這個方法就可以在Binder死亡的時候收到通知了镊靴。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mRemoteBookManager == null) return;
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
// TODO:這里重新綁定遠程Service
}
};
在客戶端綁定遠程服務(wù)成功之后铣卡,給binder設(shè)置死亡代理
mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
(8)android中的ipc方式
1链韭、 使用Bundle
Bundle實現(xiàn)了Parcelable接口,Activity煮落、Service和Receiver都支持在Intent中傳遞Bundle數(shù)據(jù)
2敞峭、 使用文件共享
這種方式簡單,適合在對數(shù)據(jù)同步要求不高的進程之間進行通信蝉仇,并且要妥善處理并發(fā)讀寫的問題旋讹,SharedPreferences是一個特例,雖然它也是文件的一種轿衔,但是由于系統(tǒng)對它的讀寫有一定的緩存策略沉迹,即在內(nèi)存中會有一份SharedPreferences文件的緩存,因此在多進程模式下害驹、系統(tǒng)對它的讀寫就變的不可靠鞭呕,當(dāng)面對高并發(fā)讀寫訪問的時候,有很大幾率會丟失宛官,因此葫松,不建議在進程間通信中使用SharedPreferences。
3底洗、 使用Messenger
Messenger是一種輕量級的IPC方案腋么,它的底層實現(xiàn)就是AIDL。Messenger是以串行的方式處理請求的亥揖,即服務(wù)端只能一個個處理珊擂,不存在并發(fā)執(zhí)行的情形。
4费变、 使用AIDL
大致流程:首先建一個Service和一個AIDL接口未玻,接著創(chuàng)建一個類繼承自AIDL接口中的Stub類中的抽象方法,在Service的onBind方法中返回這個類的對象胡控,然后客戶端就可以綁定服務(wù)端Service扳剿,建立連接后就可以訪問遠程服務(wù)端的方法了。 1.AIDL支持的數(shù)據(jù)類型:基本數(shù)據(jù)類型昼激、String和CharSequence庇绽、ArrayList、HashMap橙困、Parcelable以及AIDL瞧掺; 2.某些類即使和AIDL文件在同一個包中也要顯式import進來; 3.AIDL中除了基本數(shù)據(jù)類凡傅,其他類型的參數(shù)都要標上方向:in辟狈、out或者inout; 4.AIDL接口中支持方法,不支持聲明靜態(tài)變量哼转; 5.為了方便AIDL的開發(fā)明未,建議把所有和AIDL相關(guān)的類和文件全部放入同一個包中,這樣做的好處是壹蔓,當(dāng)客戶端是另一個應(yīng)用的時候趟妥,可以直接把整個包復(fù)制到客戶端工程中。 6.RemoteCallbackList是系統(tǒng)專門提供的用于刪除跨進程Listener的接口佣蓉。RemoteCallbackList是一個泛型披摄,支持管理任意的AIDL接口,因為所有的AIDL接口都繼承自IInterface接口勇凭。
5疚膊、使用ContentProvider
1.ContentProvider主要以表格的形式來組織數(shù)據(jù),并且可以包含多個表虾标; 2.ContentProvider還支持文件數(shù)據(jù)寓盗,比如圖片、視頻等夺巩,系統(tǒng)提供的MediaStore就是文件類型的ContentProvider; 3.ContentProvider對底層的數(shù)據(jù)存儲方式?jīng)]有任何要求周崭,可以是SQLite柳譬、文件,甚至是內(nèi)存中的一個對象都行续镇; 4.要觀察ContentProvider中的數(shù)據(jù)變化情況美澳,可以通過ContentResolver的registerContentObserver方法來注冊觀察者;
6摸航、使用Socket
套接字制跟,分為流式套接字和用戶數(shù)據(jù)報套接字兩種,分別對應(yīng)于網(wǎng)絡(luò)的傳輸控制層中TCP和UDP協(xié)議酱虎。
TCP協(xié)議是面向連接的協(xié)議雨膨,提供穩(wěn)定的雙向通信功能,TCP連接的建立需要經(jīng)過"三次握手"才能完成读串,為了提供穩(wěn)定的數(shù)據(jù)傳輸功能聊记,其本身提供了超時重傳功能,因此具有很高的穩(wěn)定性
UDP是無連接的恢暖,提供不穩(wěn)定的單向通信功能排监,當(dāng)然UDP也可以實現(xiàn)雙向通信功能,在性能上杰捂,UDP具有更好的效率舆床,其缺點是不保證數(shù)據(jù)能夠正確傳輸,尤其是在網(wǎng)絡(luò)擁塞的情況下。
Binder連接池
7挨队、BinderPool
當(dāng)項目規(guī)模很大的時候谷暮,創(chuàng)建很多個Service是不對的做法,因為service是系統(tǒng)資源瞒瘸,太多的service會使得應(yīng)用看起來很重坷备,所以最好是將所有的AIDL放在同一個Service中去管理。整個工作機制是:每個業(yè)務(wù)模塊創(chuàng)建自己的AIDL接口并實現(xiàn)此接口情臭,這個時候不同業(yè)務(wù)模塊之間是不能有耦合的省撑,所有實現(xiàn)細節(jié)我們要單獨開來,然后向服務(wù)端提供自己的唯一標識和其對應(yīng)的Binder對象俯在;對于服務(wù)端來說竟秫,只需要一個Service,服務(wù)端提供一個queryBinder接口跷乐,這個接口能夠根據(jù)業(yè)務(wù)模塊的特征來返回相應(yīng)的Binder對象給它們肥败,不同的業(yè)務(wù)模塊拿到所需的Binder對象后就可以進行遠程方法調(diào)用了。 Binder連接池的主要作用就是將每個業(yè)務(wù)模塊的Binder請求統(tǒng)一轉(zhuǎn)發(fā)到遠程Service去執(zhí)行愕提,從而避免了重復(fù)創(chuàng)建Service的過程馒稍。
建議在AIDL開發(fā)工作中引入BinderPool機制。
8.AIDL總結(jié)
aidl的核心是代理類proxy里的實現(xiàn)方法,里面通過t'ransact方法調(diào)動起服務(wù)端的讀寫數(shù)據(jù)和調(diào)用方法動作,而proxy的創(chuàng)建依賴于IBinder,這個IBinder來自于stub的實現(xiàn)類對象,實現(xiàn)了aidl里的方法