IPC(進程間通信或者跨進程通信):指兩個進程間交換數(shù)據(jù)的過程棠耕。
線程:線程是CPU調(diào)度的最小單位吹零,是一種有限的系統(tǒng)資源漫雕。
進程:進程一般指一個執(zhí)行單元滨嘱,在PC或者移動設備上指一個程序或者應用。
一個進程可以包含多個線程浸间,最簡單情況下太雨,一個進程只包含一個線程(即主線程)。Android中主線程也叫作UI線程魁蒜。
Android中的多進程模式
Android中只能通過給四大組件指定 android:process 屬性囊扳,來可以開啟多進程模式
特殊情況,還有一種非常規(guī)的多進程方法:通過JNI在native層去fork一個新進程
開啟多進程模式
正常情況下兜看,在Android中多進程是指一個應用中存在多個進程的情況锥咸。
在指定android:process屬性值里面指定當前組件運行在那個進程,指定的時候其中的“ : ”表示在當前進程前附加當前包名细移,另一種就是完整的聲明方式搏予,直接直接定具體進行名
多進程模式的運行機制
所有運行在不同進程中的四大組件,只要他們之間需要通過內(nèi)存來共享數(shù)據(jù)弧轧,都會共享失敗雪侥,這也是多進程帶來的主要影響。
一般來說精绎,使用多進程會造成如下幾方面問題:
- (1)靜態(tài)成員和單例模式完全失效
- (2)線程同步機制完全失效校镐,不同進程針對的是不同對象
- (3)SharedPreference可靠性下降,不支持兩個進程同時執(zhí)行寫操作捺典,否則會造成一定幾率的數(shù)據(jù)丟失
- (4)Application會多次創(chuàng)建
IPC基礎概念
主要包含三個方面內(nèi)容:serializable接口鸟廓、Parcelable接口、Binder
serializable和Parcelable接口襟己,可以完成對象的序列化過程引谜,當我么你需要通過Intent和Binder傳輸數(shù)據(jù)時就需要用到
Serializable接口
Serializable接口是Java所提供的一個序列化接口,它是一個空接口擎浴,為對象提供標準的序列化和反序列化操作员咽。使用的時候在類的聲明中實現(xiàn)Serializable接口即可,或者再聲明一個私有靜態(tài)常量serialVersionUID贮预,系統(tǒng)會自動實現(xiàn)默認序列化贝室。
serialVersionUID作用:序列化的時候?qū)懭胛募敺葱蛄谢臅r候系統(tǒng)會去檢測文件中的serialVersionUID是否一致滑频,如果一致就說明序列化的類版本和當前類的版本相同,這時候可成功反序列化银伟;否則就說明發(fā)生了一些改變,就無法反序列化。
如何進行對象的序列化和反序列化圆米,只需要采用ObjectOutPutStream和ObjectInputStream即可
需要注意的是:靜態(tài)成員變量屬于類,不屬于對象桂肌,所以不會參與序列化過程;其次還可以使用transient關鍵字標記成員變量不參與序列化過程。
另外螃宙,序列化和反序列化的實現(xiàn)過程也是可以改變的芹助,需要重寫writeObject()和readObject()方法
Parcelable接口
Parcelable接口无蜂,只要實現(xiàn)這個接口,一個類的對象就可以實現(xiàn)序列化,并通過Intent和Binder傳遞琉朽。
首先說一下Parcel,內(nèi)部包裝了可序列化的數(shù)據(jù),可以在Binder中自由傳輸,在序列化過程中峡钓,需要實現(xiàn)的功能有序列化、反序列化和功能描述辈赋。
序列化功能由writeToParcel()完成,最終通過Parcel的一系列write方法來完成。
反序列化功能由CREATOR完成鸦泳,其內(nèi)部標明了如何創(chuàng)建序列化對象和數(shù)組,并通過Parcel的一些列read方法來完成
內(nèi)容描述功能由describeContents方法完成鼎姐,幾乎所有情況這個方法都應該返回0钾麸,僅當當前對象中存在文件描述符時,返回1
Serializable和Parcelable的區(qū)別
- Serializable是Java中的序列化接口炕桨,使用簡單但是開銷大饭尝,序列化和反序列化過程需要大量I/O。會創(chuàng)建大量的臨時變量献宫,從而引起頻繁GC
- Parcelable是Android中的序列化方式钥平,缺點是使用稍微麻煩點,但是效率高姊途。主要用在內(nèi)存序列化上涉瘾,通過Parcelable將對象序列化到存儲設備中或者將對象序列化過后通過網(wǎng)絡傳輸,但是這個過程稍微復雜捷兰,所以這兩種情況推薦使用Serializable
Binder(使用和上層原理)
直觀來說立叛,Binder是Android的一個類,實現(xiàn)了Binder接口贡茅。從IPC角度來說秘蛇,Binder是Android中的一種跨進程通信方式。
Android開發(fā)中友扰,Binder主要用于Service中彤叉,包括AIDL和Messenger庶柿,其中普通的Service中不涉及進程通信村怪,就比較簡單,而Messenger底層是AIDL浮庐,所以針對AIDL來講解Binder
創(chuàng)建AIDL文件后甚负,系統(tǒng)會自動生成一個對應的Java類,其中包含兩個核心實現(xiàn)類审残,內(nèi)部stub類和stub的內(nèi)部類Proxy梭域,這兩個類的每個方法含義詳解如下:
- DESCRIPTOR:Binder的唯一標識,一般用當前Binder的類名表示
- asInterface(android.os.IBinder obj):用于將服務器的Binder對象轉(zhuǎn)換成客戶端所需AIDL接口類型對象搅轿,這種轉(zhuǎn)換過程是區(qū)分進程的病涨,如果客戶端和服務器位于統(tǒng)一進程,那么此方法返回的就是服務器端的Stub對象本身璧坟,否則就是返回系統(tǒng)封裝的Stub.Proxy對象
- asBinder:用于返回當前Binder對象
- onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flag):此方法運行在服務端的Binder線程中既穆,當服務器發(fā)起跨進程請求時赎懦,遠程請求會通過系統(tǒng)底層封裝后交由此方法處理。code用于確定客戶端所請求的目標方法是什么幻工,接著從data中取出目標方法所需參數(shù)励两,然后執(zhí)行目標方法,執(zhí)行完成后返回值寫入reply中囊颅。如果此方法返回false当悔,那么客戶端請求失敗
- Proxy#XXX方法:運行在客戶端,當客戶端遠程調(diào)用此方法時:內(nèi)部會首先創(chuàng)建該方法所需要的輸入型Parcel對象_data踢代、輸出型Parcel對象_reply和返回值對象盲憎,然后將參數(shù)信息寫入_data中(如果有參數(shù)的話),接著調(diào)用onTransact()發(fā)起RPC請求(遠程過程調(diào)用)奸鬓,同時當前線程掛起焙畔;然后服務器onTransact()方法調(diào)用,直到RPC返回后當前線程繼續(xù)執(zhí)行串远。
注意:當客戶端發(fā)起遠程請求時宏多,由于當前線程會被掛起直至服務端進程喚醒并返回數(shù)據(jù),所以如果遠程方法很耗時澡罚,那么不能在UI線程中發(fā)起遠程請求伸但;由于服務器的Binder方法運行在Binder線程池中,所以不管Binder方法是否耗時都應采用同步的方式實現(xiàn)
Binder設置死亡代理
1留搔、首先聲明一個DeathRecipient對象更胖,DeathRecipient是一個接口,內(nèi)部只有一個方法binderDied隔显,當binder死亡時却妨,就會調(diào)用此方法,然后移除之前的Binderd代理并重新綁定遠程服務括眠。
2彪标、然后在客戶端綁定遠程服務后,給binder設置死亡代理:binder.linkTodeath(DeathRecipient, 標志位)
這樣設置好過后掷豺,當Binder死亡過后捞烟,就可以收到通知;另外還可以通過Binder的isBinderAlive()判斷Binder是否死亡当船。
Android中的IPC方式
1题画、Bundle
四大組件中Activity、Service德频、Receiver都支持在Intent中傳遞Bundle數(shù)據(jù)苍息,由于Bundle實現(xiàn)了Parcelable接口,所以可以在不同進程間傳遞。
Bundle支持的數(shù)據(jù)類型:基本數(shù)據(jù)類型竞思,實現(xiàn)了Parcelable接口和Serializable接口的對象桌粉,以及Android支持的特殊對象。
2衙四、文件共享
文件共享也是一種進程間的通信方式铃肯,兩個進程通過讀寫同一文件來交換數(shù)據(jù)。Windows上传蹈,一個文件如果被加了排斥鎖就會導致其他線程無法訪問其數(shù)據(jù)押逼,而Android基于Linux,并沒有對并非讀寫文件有所限制惦界。
文件共享方式共享數(shù)據(jù)對文件格式?jīng)]有具體要求隐绵,只要讀寫雙方約定數(shù)據(jù)格式即可搀庶。這種方式也是有局限性的,比如說:并發(fā)讀寫時,獲取的數(shù)據(jù)可能不是最新的潭千。因此需要妥善處理并發(fā)讀寫的問題友驮。
SharedPreference是個特例疼鸟,在多進程下殴蓬,讀寫操作就會變得不可靠,面對高并發(fā)的讀寫訪問就可能丟失數(shù)據(jù)狂窑。因此不建議在進程通信中使用SharedPreference
3媳板、Messenger
Messenger可以在不同進程中傳遞Message對象,在Message中放入需要傳遞的數(shù)據(jù)泉哈。
Messenger是一種輕量級的IPC方案蛉幸,底層實現(xiàn)是AIDL,對AIDL進行了封裝丛晦。
實現(xiàn)Messenger步驟奕纫,分為客戶端和服務器端:
- 服務端進程
服務端創(chuàng)建一個Service來處理客戶端的連接請求,同時創(chuàng)建一個Handler并通過它創(chuàng)建一個Messenger對象烫沙,然后再Service的onBind方法中返回這個Messenger對象底層的Binder即可匹层。 -
客戶端進程
客戶端進程首先綁定服務端的Service,綁定成功后用服務端返回的IBinder對象創(chuàng)建一個Messenger斧吐,通過這個Messenger向服務器端發(fā)送消息又固,消息類型為Message對象仲器。如果需要服務端回應煤率,則需要在客戶端創(chuàng)建一個Handler,并創(chuàng)建一個新的Messenger乏冀,并把這個Messenger對象通過Message的replyTo參數(shù)傳遞給服務端蝶糯,服務端通過這個replyTo就可以回應客戶端。
Messenger工作原理
4辆沦、AIDL
AIDL實現(xiàn)進程通信流程分為客戶端和服務端:
- 1昼捍、服務端
服務端創(chuàng)建一個Service監(jiān)聽客戶端的連接請求识虚,然后創(chuàng)建一個AIDL文件,將暴露給客戶端的接口在這個AIDL文件中聲明妒茬,最后在Service中實現(xiàn)這個接口 - 2担锤、客戶端
綁定服務端的Service,綁定成功后乍钻,將服務端返回的Binder對象轉(zhuǎn)換成AIDL接口所屬類型肛循,然后就可以調(diào)用AIDL中的方法。 - 3银择、AIDL接口的創(chuàng)建
在AIDL文件中多糠,AIDL所支持的數(shù)據(jù)類型:
(1)基本數(shù)據(jù)類型
(2)String和CharSequence
(3)List:只支持ArrayList,里面每個元素都必須能夠被AIDL支持
(4)Map:只支持HashMap浩考,里面的每個元素都必須被AIDL支持夹孔,包括key和value
(5)Parcelable:所有實現(xiàn)了Parcelable的對象
(6)AIDL:所有AIDL接口本身也可以在AIDL中使用
后續(xù)兩種數(shù)據(jù)需要顯示的import導入。
5析孽、使用ContentProvider
ContentProvider是Android專門提供用于不同進程間數(shù)據(jù)共享的方式搭伤,底層實現(xiàn)同樣是Binder
ContentProvider的使用參考——Android第一行代碼
6、使用Socket
Socket稱為套接字袜瞬,網(wǎng)絡通信中的概念闷畸,分為流式套接字和用戶數(shù)據(jù)報套接字也就是TCP和UDP
Binder連接池
AIDL使用大致流程:首先創(chuàng)建一個Service和AIDL接口,接著創(chuàng)建一個類繼承自AIDL接口中的Stub類并實現(xiàn)Stub中的抽象方法吞滞,在Service的onBind方法中返回這個類的對象佑菩,然后客戶端就可以綁定服務端的Service,建立連接后就可以訪問遠程服務端的方法了裁赠。
Binder連接池的主要作用:將每個業(yè)務模塊的Binder請求統(tǒng)一轉(zhuǎn)發(fā)到遠程Service中執(zhí)行殿漠,從而避免了重復創(chuàng)建Service的過程。