摘要:什么是機制機制,含義為進程間通信或者跨進程通信,是指兩個進程之間進行數據交換的過程。線程同步機制完全失效。五使用是中提供的專門用于不同應用間進行數據共享的方式,從這一點來看,它天生就適合進程間通信。
什么是IPC機制?
IPC機制,含義為進程間通信或者跨進程通信,是指兩個進程之間進行數據交換的過程。
任何一個操作系統,線程是CPU可以操作的最小單元,同時線程是一種有限的系統資源。而進程一般指一個執行單元,在PC和移動設備上指一個程序或者一個應用。一個進程可以包含多個線程,因此進程和線程是包含與被包含的關系。
Android多進程模式正常情況下,在Android中多進程是指一個應用中存在多個進程的情況,因此這里不討論兩個應用之間的多進程情況。首先,在Android中使用多進程只有一種方法,那就是給四大組件(Activity、Service、Receiver、ContentProvider)在AndroidMenifest中指定android:process屬性,除此之外沒有其他辦法,也就是說我們無法給一個線程或者一個實體類指定其運行時所在的進程。其實還有另一種非常規的多進程方法,那就是通過JNI在native層去fork一個新的進程,但是這種方法屬于特殊情況,也不是常用的創建多進程的方式,因此我們暫時不考慮這種方式。
???????? ???????????????????? ???????????????????? ????????
上述代碼中就出現了三個進程,上面的示例分別為SecondActivity和ThirdActivity指定了process屬性,并且它們的屬性值不同,這意味著當前應用又增加了兩個新進程。
假設當前應用的包名為“com.ryg.chapter_2”,
當SecondActivity啟動時,系統會為它創建一個多帶帶的進程,進程名為“com.ryg.chapter_2:remote”;
當ThirdActivity啟動時,系統也會為它創建一個多帶帶的進程,進程名為“com.ryg.chapter_2.remote”。
同時入口Activity是MainActivity,沒有為它指定process屬性,那么它運行在默認進程中,默認進程的進程名是包名。
但是,實際使用中多進程是有很多問題需要處理的,例如以下問題:
(1)靜態成員和單例模式完全失效。
(2)線程同步機制完全失效。
(3)SharedPreferences的可靠性下降。
(4)Application會多次創建。
簡單來說,第(1),(2)問題都是因為多進程的內存塊不同,所以數據共享也會出現問題;第三個是因為出現多進程,那么對SharedPreferences同時讀寫,會出現
數據不同步的安全問題;第(4),每一次開啟多進程,相當于重啟了一個應用程序,所以application一定會重走一次。
IPC機制實現的基礎 一、Serializable接口首先,Serializable接口是一個空接口,作用是用來處理序列化的,為對象提供標準的序列化和反序列化操作。
讓一個對象實現序列化,只需要這個類實現Serializable接口并聲明一個serialVersionUID即可,實際上,甚至這個serialVersionUID也不是必需的,我們不聲明這個serialVersionUID同樣也可以實現序列化,但是會對反序列化造成一定不確定因素。
例如:
public?class?User?implements?Serializable?{ ?????????private?static?final?long?serialVersionUID?=?519067123721295773L; ?????????public?int?userId; ?????????public?String?userName; ?????????public?boolean?isMale; ????????... ????}
實際上,進行對象的序列化和反序列化也非常簡單,只需要采用ObjectOutputStream和ObjectInputStream即可輕松實現:
//序列化過程 ????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();
所謂的序列化,就是把實現了Serializable接口的User對象寫到文件中就可以快速恢復了,恢復后的對象newUser和user的內容完全一樣,但是兩者并不是同一個對象。
那么現在,serialVersionUID的作用就體現出來了,這個serialVersionUID是用來輔助序列化和反序列化過程的,原則上序列化后的數據中的serialVersionUID只有和當前類的serialVersionUID相同才能夠正常地被反序列化。
serialVersionUID的詳細工作機制是這樣的:序列化的時候系統會把當前類的serialVersionUID寫入序列化的文件中(也可能是其他中介),當反序列化的時候系統會去檢測文件中的serialVersionUID,看它是否和當前類的serialVersionUID一致,如果一致就說明序列化的類的版本和當前類的版本是相同的,這個時候可以成功反序列化;否則就說明當前類和序列化的類相比發生了某些變換,比如成員變量的數量、類型可能發生了改變,這個時候是無法正常反序列化的。
二、Parcelable接口Parcelable也是一個接口,只要實現這個接口,一個類的對象就可以實現序列化并可以通過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()); ?????????} ????} ????????
這里先說一下Parcel,Parcel內部包裝了可序列化的數據,可以在Binder中自由傳輸。從上述代碼中可以看出,在序列化過程中需要實現的功能有序列化、反序列化和內容描述。
序列化功能由writeToParcel方法來完成,最終是通過Parcel中的一系列write方法來完成的;
反序列化功能由CREATOR來完成,其內部標明了如何創建序列化對象和數組,并通過Parcel的一系列read方法來完成反序列化過程;
內容描述功能由describeContents方法來完成,幾乎在所有情況下這個方法都應該返回0,僅當當前對象中存在文件描述符時,此方法返回1。
Parcelable和Serializable和區別
Serializable是Java中的序列化接口,其使用起來簡單但是開銷很大,序列化和反序列化過程需要大量I/O操作。而Parcelable是Android中的序列化方式,因此更適合用在Android平臺上,它的缺點就是使用起來稍微麻煩點,但是它的效率很高,這是Android推薦的序列化方式,因此我們要首選Parcelable。Parcelable主要用在內存序列化上,通過Parcelable將對象序列化到存儲設備中或者將對象序列化后通過網絡傳輸也都是可以的,但是這個過程會稍顯復雜,因此在這兩種情況下建議大家使用Serializable。
Android中的IPC方式 一、使用Bundle四大組件中的三大組件(Activity、Service、Receiver)都是支持在Intent中傳遞Bundle數據的,由于Bundle實現了Parcelable接口,所以它可以方便地在不同的進程間傳輸。基于這一點,當我們在一個進程中啟動了另一個進程的Activity、Service和Receiver,我們就可以在Bundle中附加我們需要傳輸給遠程進程的信息并通過Intent發送出去。當然,我們傳輸的數據必須能夠被序列化,比如基本類型、實現了Parcellable接口的對象、實現了Serializable接口的對象以及一些Android支持的特殊對象
二、使用文件共享共享文件也是一種不錯的進程間通信方式,兩個進程通過讀/寫同一個文件來交換數據,比如A進程把數據寫入文件,B進程通過讀取這個文件來獲取數據。
但是,我們知道,通過文件共享這種方式來共享數據對文件格式是沒有具體要求的,比如可以是文本文件,也可以是XML文件,只要讀/寫雙方約定數據格式即可。通過文件共享的方式也是有局限性的,比如并發讀/寫的問題。
當然,SharedPreferences是個特例,眾所周知,SharedPreferences是Android中提供的輕量級存儲方案,它通過鍵值對的方式來存儲數據,在底層實現上它采用XML文件來存儲鍵值對,每個應用的SharedPreferences文件都可以在當前包所在的data目錄下查看到。一般來說,它的目錄位于/data/data/package name/shared_prefs目錄下,其中package name表示的是當前應用的包名。從本質上來說,SharedPreferences也屬于文件的一種,但是由于系統對它的讀/寫有一定的緩存策略,即在內存中會有一份SharedPreferences文件的緩存,因此在多進程模式下,系統對它的讀/寫就變得不可靠,當面對高并發的讀/寫訪問,Sharedpreferences有很大幾率會丟失數據,因此,不建議在進程間通信中使用SharedPreferences。
三、使用MessengerMessenger是一種輕量級的IPC方案,它的底層實現是AIDL。
注意:這里的Messenger,不同于handler,handler是無法在進程間通信的。
實現一個Messenger有如下幾個步驟,分為服務端和客戶端。
服務端
我們需要在服務端創建一個Service來處理客戶端的連接請求,同時創建一個Handler并通過它來創建一個Messenger對象,然后在Service的onBind中返回這個Messenger對象底層的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(); ?????????} ????}
客戶端
客戶端進程中,首先要綁定服務端的Service,綁定成功后用服務端返回的IBinder對象創建一個Messenger,通過這個Messenger就可以向服務端發送消息了,發消息類型為Message對象。
如果需要服務端能夠回應客戶端,就和服務端一樣,我們還需要創建一個Handler并創建一個新的Messenger,并把這個Messenger對象通過Message的replyTo參數傳遞給服務端,服務端通過這個replyTo參數就可以回應客戶端。
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來進行進程間通信的方法,如果大量的消息同時發送到服務端,服務端仍然只能一個個處理,如果有大量的并發請求,那么用Messenger就不太合適了。
所以,我們可能需要跨進程調用服務端的方法,這種情形用Messenger就無法做到了,但是我們可以使用AIDL來實現跨進程的方法調用。
這里,就不寫出AIDL的實現方式了,這個示例網絡資源很多。
五、使用ContentProviderContentProvider是Android中提供的專門用于不同應用間進行數據共享的方式,從這一點來看,它天生就適合進程間通信。和Messenger一樣,ContentProvider的底層實現同樣也是Binder。
六、使用使用Socket我們通過Socket來實現進程間的通信。Socket也稱為“套接字”,是網絡通信中的概念,它分為流式套接字和用戶數據報套接字兩種,分別對應于網絡的傳輸控制層中的TCP和UDP協議。TCP協議是面向連接的協議,提供穩定的雙向通信功能,TCP連接的建立需要經過“三次握手”才能完成,為了提供穩定的數據傳輸功能,其本身提供了超時重傳機制,因此具有很高的穩定性;而UDP是無連接的,提供不穩定的單向通信功能,當然UDP也可以實現雙向通信功能。在性能上,UDP具有更好的效率,其缺點是不保證數據一定能夠正確傳輸,尤其是在網絡擁塞的情況下。
總之,上面六種都可以實現進程間通信的,具體可以根據自己的需求來選擇合適的方式。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69035.html
摘要:中為何新增來作為主要的方式運行機制是怎樣的機制有什么優勢運行機制是怎樣的基于通信模式,除了端和端,還有兩角色一起合作完成進程間通信功能。 目錄介紹 2.0.0.1 什么是Binder?為什么要使用Binder?Binder中是如何進行線程管理的?總結binder講的是什么? 2.0.0.2 Android中進程和線程的關系?什么是IPC?為何需要進行IPC?多進程通信可能會出現什么問...
摘要:而使用綁定的方式啟動則可以實現之間的通信。下面就講述一下綁定實現本地通信的流程。單擊調用方法并且解除綁定。注意解除綁定事件只能執行一次,否則程序會崩潰。 寫作原因:跨進程通信的實現和理解是Android進階中重要的一環。下面博主分享IPC一些相關知識、操作及自己在學習IPC過程中的一些理解。 這一章是為下面的Messenger和AIDL的使用做準備,主要講解Android Servic...
閱讀 4122·2022-09-16 13:49
閱讀 1398·2021-11-22 15:12
閱讀 1519·2021-09-09 09:33
閱讀 1039·2019-08-30 13:15
閱讀 1720·2019-08-29 15:30
閱讀 654·2019-08-27 10:52
閱讀 2643·2019-08-26 17:41
閱讀 1896·2019-08-26 12:11