摘要:什么是機(jī)制機(jī)制,含義為進(jìn)程間通信或者跨進(jìn)程通信,是指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過(guò)程。線程同步機(jī)制完全失效。五使用是中提供的專門用于不同應(yīng)用間進(jìn)行數(shù)據(jù)共享的方式,從這一點(diǎn)來(lái)看,它天生就適合進(jìn)程間通信。
什么是IPC機(jī)制?
IPC機(jī)制,含義為進(jìn)程間通信或者跨進(jìn)程通信,是指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過(guò)程。
任何一個(gè)操作系統(tǒng),線程是CPU可以操作的最小單元,同時(shí)線程是一種有限的系統(tǒng)資源。而進(jìn)程一般指一個(gè)執(zhí)行單元,在PC和移動(dòng)設(shè)備上指一個(gè)程序或者一個(gè)應(yīng)用。一個(gè)進(jìn)程可以包含多個(gè)線程,因此進(jìn)程和線程是包含與被包含的關(guān)系。
Android多進(jìn)程模式正常情況下,在Android中多進(jìn)程是指一個(gè)應(yīng)用中存在多個(gè)進(jìn)程的情況,因此這里不討論兩個(gè)應(yīng)用之間的多進(jìn)程情況。首先,在Android中使用多進(jìn)程只有一種方法,那就是給四大組件(Activity、Service、Receiver、ContentProvider)在AndroidMenifest中指定android:process屬性,除此之外沒(méi)有其他辦法,也就是說(shuō)我們無(wú)法給一個(gè)線程或者一個(gè)實(shí)體類指定其運(yùn)行時(shí)所在的進(jìn)程。其實(shí)還有另一種非常規(guī)的多進(jìn)程方法,那就是通過(guò)JNI在native層去fork一個(gè)新的進(jìn)程,但是這種方法屬于特殊情況,也不是常用的創(chuàng)建多進(jìn)程的方式,因此我們暫時(shí)不考慮這種方式。
???????? ???????????????????? ???????????????????? ????????
上述代碼中就出現(xiàn)了三個(gè)進(jìn)程,上面的示例分別為SecondActivity和ThirdActivity指定了process屬性,并且它們的屬性值不同,這意味著當(dāng)前應(yīng)用又增加了兩個(gè)新進(jìn)程。
假設(shè)當(dāng)前應(yīng)用的包名為“com.ryg.chapter_2”,
當(dāng)SecondActivity啟動(dòng)時(shí),系統(tǒng)會(huì)為它創(chuàng)建一個(gè)多帶帶的進(jìn)程,進(jìn)程名為“com.ryg.chapter_2:remote”;
當(dāng)ThirdActivity啟動(dòng)時(shí),系統(tǒng)也會(huì)為它創(chuàng)建一個(gè)多帶帶的進(jìn)程,進(jìn)程名為“com.ryg.chapter_2.remote”。
同時(shí)入口Activity是MainActivity,沒(méi)有為它指定process屬性,那么它運(yùn)行在默認(rèn)進(jìn)程中,默認(rèn)進(jìn)程的進(jìn)程名是包名。
但是,實(shí)際使用中多進(jìn)程是有很多問(wèn)題需要處理的,例如以下問(wèn)題:
(1)靜態(tài)成員和單例模式完全失效。
(2)線程同步機(jī)制完全失效。
(3)SharedPreferences的可靠性下降。
(4)Application會(huì)多次創(chuàng)建。
簡(jiǎn)單來(lái)說(shuō),第(1),(2)問(wèn)題都是因?yàn)槎噙M(jìn)程的內(nèi)存塊不同,所以數(shù)據(jù)共享也會(huì)出現(xiàn)問(wèn)題;第三個(gè)是因?yàn)槌霈F(xiàn)多進(jìn)程,那么對(duì)SharedPreferences同時(shí)讀寫,會(huì)出現(xiàn)
數(shù)據(jù)不同步的安全問(wèn)題;第(4),每一次開(kāi)啟多進(jìn)程,相當(dāng)于重啟了一個(gè)應(yīng)用程序,所以application一定會(huì)重走一次。
IPC機(jī)制實(shí)現(xiàn)的基礎(chǔ) 一、Serializable接口首先,Serializable接口是一個(gè)空接口,作用是用來(lái)處理序列化的,為對(duì)象提供標(biāo)準(zhǔn)的序列化和反序列化操作。
讓一個(gè)對(duì)象實(shí)現(xiàn)序列化,只需要這個(gè)類實(shí)現(xiàn)Serializable接口并聲明一個(gè)serialVersionUID即可,實(shí)際上,甚至這個(gè)serialVersionUID也不是必需的,我們不聲明這個(gè)serialVersionUID同樣也可以實(shí)現(xiàn)序列化,但是會(huì)對(duì)反序列化造成一定不確定因素。
例如:
public?class?User?implements?Serializable?{ ?????????private?static?final?long?serialVersionUID?=?519067123721295773L; ?????????public?int?userId; ?????????public?String?userName; ?????????public?boolean?isMale; ????????... ????}
實(shí)際上,進(jìn)行對(duì)象的序列化和反序列化也非常簡(jiǎn)單,只需要采用ObjectOutputStream和ObjectInputStream即可輕松實(shí)現(xiàn):
//序列化過(guò)程 ????User?user?=?new?User(0,"jake",true); ????ObjectOutputStream?out?=?new?ObjectOutputStream( ????????????????new?FileOutputStream("cache.txt")); ????out.writeObject(user); ????out.close(); ????//反序列化過(guò)程 ????ObjectInputStream?in?=?new?ObjectInputStream( ????????????????new?FileInputStream("cache.txt")); ????User?newUser?=?(User)?in.readObject(); ????in.close();
所謂的序列化,就是把實(shí)現(xiàn)了Serializable接口的User對(duì)象寫到文件中就可以快速恢復(fù)了,恢復(fù)后的對(duì)象newUser和user的內(nèi)容完全一樣,但是兩者并不是同一個(gè)對(duì)象。
那么現(xiàn)在,serialVersionUID的作用就體現(xiàn)出來(lái)了,這個(gè)serialVersionUID是用來(lái)輔助序列化和反序列化過(guò)程的,原則上序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類的serialVersionUID相同才能夠正常地被反序列化。
serialVersionUID的詳細(xì)工作機(jī)制是這樣的:序列化的時(shí)候系統(tǒng)會(huì)把當(dāng)前類的serialVersionUID寫入序列化的文件中(也可能是其他中介),當(dāng)反序列化的時(shí)候系統(tǒng)會(huì)去檢測(cè)文件中的serialVersionUID,看它是否和當(dāng)前類的serialVersionUID一致,如果一致就說(shuō)明序列化的類的版本和當(dāng)前類的版本是相同的,這個(gè)時(shí)候可以成功反序列化;否則就說(shuō)明當(dāng)前類和序列化的類相比發(fā)生了某些變換,比如成員變量的數(shù)量、類型可能發(fā)生了改變,這個(gè)時(shí)候是無(wú)法正常反序列化的。
二、Parcelable接口Parcelable也是一個(gè)接口,只要實(shí)現(xiàn)這個(gè)接口,一個(gè)類的對(duì)象就可以實(shí)現(xiàn)序列化并可以通過(guò)Intent和Binder傳遞。
public?class?User?implements?Parcelable?{ ?????????public?int?userId; ?????????public?String?userName; ?????????public?boolean?isMale; ?????????public?Book?book; ?????????public?User(int?userId,String?userName,boolean?isMale)?{ ?????????????this.userId?=?userId; ?????????????this.userName?=?userName; ?????????????this.isMale?=?isMale; ?????????} ?????????public?int?describeContents()?{ ?????????????return?0; ?????????} ?????????public?void?writeToParcel(Parcel?out,int?flags)?{ ?????????????out.writeInt(userId); ?????????????out.writeString(userName); ?????????????out.writeInt(isMale???1?:?0); ?????????????out.writeParcelable(book,0); ?????????} ?????????public?static?final?Parcelable.Creator?CREATOR?=?new?Parcelable. ?????????Creator ()?{ ?????????????public?User?createFromParcel(Parcel?in)?{ ?????????????????return?new?User(in); ?????????????} ?????????????public?User[]?newArray(int?size)?{ ?????????????????return?new?User[size]; ?????????????} ?????????}; ?????????private?User(Parcel?in)?{ ?????????????userId?=?in.readInt(); ?????????????userName?=?in.readString(); ?????????????isMale?=?in.readInt()?==?1; ?????????????book?=?in.readParcelable(Thread.currentThread().getContextClass- ?????????????Loader()); ?????????} ????} ????????
這里先說(shuō)一下Parcel,Parcel內(nèi)部包裝了可序列化的數(shù)據(jù),可以在Binder中自由傳輸。從上述代碼中可以看出,在序列化過(guò)程中需要實(shí)現(xiàn)的功能有序列化、反序列化和內(nèi)容描述。
序列化功能由writeToParcel方法來(lái)完成,最終是通過(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。
Parcelable和Serializable和區(qū)別
Serializable是Java中的序列化接口,其使用起來(lái)簡(jiǎn)單但是開(kāi)銷很大,序列化和反序列化過(guò)程需要大量I/O操作。而Parcelable是Android中的序列化方式,因此更適合用在Android平臺(tái)上,它的缺點(diǎn)就是使用起來(lái)稍微麻煩點(diǎn),但是它的效率很高,這是Android推薦的序列化方式,因此我們要首選Parcelable。Parcelable主要用在內(nèi)存序列化上,通過(guò)Parcelable將對(duì)象序列化到存儲(chǔ)設(shè)備中或者將對(duì)象序列化后通過(guò)網(wǎng)絡(luò)傳輸也都是可以的,但是這個(gè)過(guò)程會(huì)稍顯復(fù)雜,因此在這兩種情況下建議大家使用Serializable。
Android中的IPC方式 一、使用Bundle四大組件中的三大組件(Activity、Service、Receiver)都是支持在Intent中傳遞Bundle數(shù)據(jù)的,由于Bundle實(shí)現(xiàn)了Parcelable接口,所以它可以方便地在不同的進(jìn)程間傳輸?;谶@一點(diǎn),當(dāng)我們?cè)谝粋€(gè)進(jìn)程中啟動(dòng)了另一個(gè)進(jìn)程的Activity、Service和Receiver,我們就可以在Bundle中附加我們需要傳輸給遠(yuǎn)程進(jìn)程的信息并通過(guò)Intent發(fā)送出去。當(dāng)然,我們傳輸?shù)臄?shù)據(jù)必須能夠被序列化,比如基本類型、實(shí)現(xiàn)了Parcellable接口的對(duì)象、實(shí)現(xiàn)了Serializable接口的對(duì)象以及一些Android支持的特殊對(duì)象
二、使用文件共享共享文件也是一種不錯(cuò)的進(jìn)程間通信方式,兩個(gè)進(jìn)程通過(guò)讀/寫同一個(gè)文件來(lái)交換數(shù)據(jù),比如A進(jìn)程把數(shù)據(jù)寫入文件,B進(jìn)程通過(guò)讀取這個(gè)文件來(lái)獲取數(shù)據(jù)。
但是,我們知道,通過(guò)文件共享這種方式來(lái)共享數(shù)據(jù)對(duì)文件格式是沒(méi)有具體要求的,比如可以是文本文件,也可以是XML文件,只要讀/寫雙方約定數(shù)據(jù)格式即可。通過(guò)文件共享的方式也是有局限性的,比如并發(fā)讀/寫的問(wèn)題。
當(dāng)然,SharedPreferences是個(gè)特例,眾所周知,SharedPreferences是Android中提供的輕量級(jí)存儲(chǔ)方案,它通過(guò)鍵值對(duì)的方式來(lái)存儲(chǔ)數(shù)據(jù),在底層實(shí)現(xiàn)上它采用XML文件來(lái)存儲(chǔ)鍵值對(duì),每個(gè)應(yīng)用的SharedPreferences文件都可以在當(dāng)前包所在的data目錄下查看到。一般來(lái)說(shuō),它的目錄位于/data/data/package name/shared_prefs目錄下,其中package name表示的是當(dāng)前應(yīng)用的包名。從本質(zhì)上來(lái)說(shuō),SharedPreferences也屬于文件的一種,但是由于系統(tǒng)對(duì)它的讀/寫有一定的緩存策略,即在內(nèi)存中會(huì)有一份SharedPreferences文件的緩存,因此在多進(jìn)程模式下,系統(tǒng)對(duì)它的讀/寫就變得不可靠,當(dāng)面對(duì)高并發(fā)的讀/寫訪問(wèn),Sharedpreferences有很大幾率會(huì)丟失數(shù)據(jù),因此,不建議在進(jìn)程間通信中使用SharedPreferences。
三、使用MessengerMessenger是一種輕量級(jí)的IPC方案,它的底層實(shí)現(xiàn)是AIDL。
注意:這里的Messenger,不同于handler,handler是無(wú)法在進(jìn)程間通信的。
實(shí)現(xiàn)一個(gè)Messenger有如下幾個(gè)步驟,分為服務(wù)端和客戶端。
服務(wù)端
我們需要在服務(wù)端創(chuàng)建一個(gè)Service來(lái)處理客戶端的連接請(qǐng)求,同時(shí)創(chuàng)建一個(gè)Handler并通過(guò)它來(lái)創(chuàng)建一個(gè)Messenger對(duì)象,然后在Service的onBind中返回這個(gè)Messenger對(duì)象底層的Binder即可。
public?class?MessengerActivity?extends?Activity?{ ?????????private?static?final?String?TAG?=?"?MessengerActivity"; ?????????private?Messenger?mService; ?????????private?ServiceConnection?mConnection?=?new?ServiceConnection()?{ ?????????????public?void?onServiceConnected(ComponentName?className,IBinder ?????????????service)?{ ?????????????????mService?=?new?Messenger(service); ?????????????????Message?msg?=?Message.obtain(null,MyConstants.MSG_FROM_CLIENT); ?????????????????Bundle?data?=?new?Bundle(); ?????????????????data.putString("msg","hello,this?is?client."); ?????????????????msg.setData(data); ?????????????????try?{ ?????????????????????mService.send(msg); ?????????????????}?catch?(RemoteException?e)?{ ?????????????????????e.printStackTrace(); ?????????????????} ?????????????} ?????????????public?void?onServiceDisconnected(ComponentName?className)?{ ?????????????} ?????????}; ?????????@Override ?????????protected?void?onCreate(Bundle?savedInstanceState)?{ ?????????????super.onCreate(savedInstanceState); ?????????????setContentView(R.layout.activity_messenger); ?????????????Intent?intent?=?new?Intent(this,MessengerService.class); ?????????????bindService(intent,mConnection,Context.BIND_AUTO_CREATE); ?????????} ?????????@Override ?????????protected?void?onDestroy()?{ ?????????????unbindService(mConnection); ?????????????super.onDestroy(); ?????????} ????}
客戶端
客戶端進(jìn)程中,首先要綁定服務(wù)端的Service,綁定成功后用服務(wù)端返回的IBinder對(duì)象創(chuàng)建一個(gè)Messenger,通過(guò)這個(gè)Messenger就可以向服務(wù)端發(fā)送消息了,發(fā)消息類型為Message對(duì)象。
如果需要服務(wù)端能夠回應(yīng)客戶端,就和服務(wù)端一樣,我們還需要?jiǎng)?chuàng)建一個(gè)Handler并創(chuàng)建一個(gè)新的Messenger,并把這個(gè)Messenger對(duì)象通過(guò)Message的replyTo參數(shù)傳遞給服務(wù)端,服務(wù)端通過(guò)這個(gè)replyTo參數(shù)就可以回應(yīng)客戶端。
public?class?MessengerActivity?extends?Activity?{ ?????????private?static?final?String?TAG?=?"?MessengerActivity"; ?????????private?Messenger?mService; ?????????private?ServiceConnection?mConnection?=?new?ServiceConnection()?{ ?????????????public?void?onServiceConnected(ComponentName?className,IBinder ?????????????service)?{ ?????????????????mService?=?new?Messenger(service); ?????????????????Message?msg?=?Message.obtain(null,MyConstants.MSG_FROM_CLIENT); ?????????????????Bundle?data?=?new?Bundle(); ?????????????????data.putString("msg","hello,this?is?client."); ?????????????????msg.setData(data); ?????????????????try?{ ?????????????????????mService.send(msg); ?????????????????}?catch?(RemoteException?e)?{ ?????????????????????e.printStackTrace(); ?????????????????} ?????????????} ?????????????public?void?onServiceDisconnected(ComponentName?className)?{ ?????????????} ?????????}; ?????????@Override ?????????protected?void?onCreate(Bundle?savedInstanceState)?{ ?????????????super.onCreate(savedInstanceState); ?????????????setContentView(R.layout.activity_messenger); ?????????????Intent?intent?=?new?Intent(this,MessengerService.class); ?????????????bindService(intent,mConnection,Context.BIND_AUTO_CREATE); ?????????} ?????????@Override ?????????protected?void?onDestroy()?{ ?????????????unbindService(mConnection); ?????????????super.onDestroy(); ?????????} ????}四、使用AIDL通信
上面的Messenger來(lái)進(jìn)行進(jìn)程間通信的方法,如果大量的消息同時(shí)發(fā)送到服務(wù)端,服務(wù)端仍然只能一個(gè)個(gè)處理,如果有大量的并發(fā)請(qǐng)求,那么用Messenger就不太合適了。
所以,我們可能需要跨進(jìn)程調(diào)用服務(wù)端的方法,這種情形用Messenger就無(wú)法做到了,但是我們可以使用AIDL來(lái)實(shí)現(xiàn)跨進(jìn)程的方法調(diào)用。
這里,就不寫出AIDL的實(shí)現(xiàn)方式了,這個(gè)示例網(wǎng)絡(luò)資源很多。
五、使用ContentProviderContentProvider是Android中提供的專門用于不同應(yīng)用間進(jìn)行數(shù)據(jù)共享的方式,從這一點(diǎn)來(lái)看,它天生就適合進(jìn)程間通信。和Messenger一樣,ContentProvider的底層實(shí)現(xiàn)同樣也是Binder。
六、使用使用Socket我們通過(guò)Socket來(lái)實(shí)現(xiàn)進(jìn)程間的通信。Socket也稱為“套接字”,是網(wǎng)絡(luò)通信中的概念,它分為流式套接字和用戶數(shù)據(jù)報(bào)套接字兩種,分別對(duì)應(yīng)于網(wǎng)絡(luò)的傳輸控制層中的TCP和UDP協(xié)議。TCP協(xié)議是面向連接的協(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ò)擁塞的情況下。
總之,上面六種都可以實(shí)現(xiàn)進(jìn)程間通信的,具體可以根據(jù)自己的需求來(lái)選擇合適的方式。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69035.html
摘要:中為何新增來(lái)作為主要的方式運(yùn)行機(jī)制是怎樣的機(jī)制有什么優(yōu)勢(shì)運(yùn)行機(jī)制是怎樣的基于通信模式,除了端和端,還有兩角色一起合作完成進(jìn)程間通信功能。 目錄介紹 2.0.0.1 什么是Binder?為什么要使用Binder?Binder中是如何進(jìn)行線程管理的?總結(jié)binder講的是什么? 2.0.0.2 Android中進(jìn)程和線程的關(guān)系?什么是IPC?為何需要進(jìn)行IPC?多進(jìn)程通信可能會(huì)出現(xiàn)什么問(wèn)...
摘要:而使用綁定的方式啟動(dòng)則可以實(shí)現(xiàn)之間的通信。下面就講述一下綁定實(shí)現(xiàn)本地通信的流程。單擊調(diào)用方法并且解除綁定。注意解除綁定事件只能執(zhí)行一次,否則程序會(huì)崩潰。 寫作原因:跨進(jìn)程通信的實(shí)現(xiàn)和理解是Android進(jìn)階中重要的一環(huán)。下面博主分享IPC一些相關(guān)知識(shí)、操作及自己在學(xué)習(xí)IPC過(guò)程中的一些理解。 這一章是為下面的Messenger和AIDL的使用做準(zhǔn)備,主要講解Android Servic...
閱讀 4184·2022-09-16 13:49
閱讀 1410·2021-11-22 15:12
閱讀 1534·2021-09-09 09:33
閱讀 1049·2019-08-30 13:15
閱讀 1737·2019-08-29 15:30
閱讀 668·2019-08-27 10:52
閱讀 2651·2019-08-26 17:41
閱讀 1907·2019-08-26 12:11