多進(jìn)程通信方式以及帶來(lái)一系列問(wèn)題
本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」,作者 Android開(kāi)發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開(kāi)發(fā)編程公眾號(hào)。
前言
今天來(lái)講解下:多進(jìn)程通信方式以及帶來(lái)的問(wèn)題,方便在項(xiàng)目中遇到問(wèn)題及時(shí)的處理;
一、Android中多進(jìn)程詳解
1、定義
- Android的多進(jìn)程通信即IPC是指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換;
- 進(jìn)程一般指一個(gè)執(zhí)行單元,在PC和移動(dòng)設(shè)備中指一個(gè)程序或應(yīng)用;
- 最簡(jiǎn)單的情況下,Android應(yīng)用中只有一個(gè)進(jìn)程,包含一個(gè)線(xiàn)程,即主線(xiàn)程,也叫作UI線(xiàn)程,只能在此線(xiàn)程更新操作UI;
- 普通情況下是不需要多進(jìn)程的,但是當(dāng)應(yīng)用需要更多的內(nèi)存或者某些特殊的Module或特殊的需求需要運(yùn)行在多進(jìn)程條件下;
2、開(kāi)啟多進(jìn)程
Android中開(kāi)啟多進(jìn)程只有一種方法,就是在AndroidManifest.xml中注冊(cè)Service、Activity、Receiver、ContentProvider時(shí)指定android:process屬性,例如:
- <service
- android:name=".MyService"
- android:process=":remote">
- </service>
- <activity
- android:name=".MyActivity"
- android:process="com.test.remote2">
- </activity>
我們?yōu)镸yService和MyActivity指定的android:process屬性值有所不同,它們的區(qū)別如下:
- :remote:以:開(kāi)頭是一種簡(jiǎn)寫(xiě),系統(tǒng)會(huì)在當(dāng)前進(jìn)程名前附件當(dāng)前包名,完整的進(jìn)程名為:com.test:remote,同時(shí)以:開(kāi)頭的進(jìn)程屬于當(dāng)前應(yīng)用的私有進(jìn)程,其它應(yīng)用的組件不能和它跑在同一進(jìn)程;
- com.test.remote2:這是完整的命名方式,不會(huì)附加包名,其它應(yīng)用如果和該進(jìn)程的ShareUID、簽名相同,則可以和它跑在同一個(gè)進(jìn)程,實(shí)現(xiàn)數(shù)據(jù)共享;
3、Android中的多進(jìn)程通信方式
多進(jìn)程通信方式主要有以下幾種,它們之間各有優(yōu)缺點(diǎn),可根據(jù)使用場(chǎng)景選擇選擇:
- AIDL:功能強(qiáng)大,支持進(jìn)程間一對(duì)多的實(shí)時(shí)并發(fā)通信,并可實(shí)現(xiàn) RPC (遠(yuǎn)程過(guò)程調(diào)用);
- Messenger:支持一對(duì)多的串行實(shí)時(shí)通信, AIDL 的簡(jiǎn)化版本;
- Bundle:四大組件的進(jìn)程通信方式,只能傳輸 Bundle 支持的數(shù)據(jù)類(lèi)型;
- ContentProvider:強(qiáng)大的數(shù)據(jù)源訪(fǎng)問(wèn)支持,主要支持 CRUD 操作,一對(duì)多的進(jìn)程間數(shù)據(jù)共享,例如我們的應(yīng)用訪(fǎng)問(wèn)系統(tǒng)的通訊錄數(shù)據(jù);
- BroadcastReceiver:即廣播,但只能單向通信,接收者只能被動(dòng)的接收消息;
- 文件共享:在非高并發(fā)情況下共享簡(jiǎn)單的數(shù)據(jù);
- Socket:通過(guò)網(wǎng)絡(luò)傳輸數(shù)據(jù);
二、 多進(jìn)程帶來(lái)的問(wèn)題
1、靜態(tài)變量失效
在一個(gè)Activity中新建一個(gè)靜態(tài)變量TEST_STATIC,并在RemoteActivity1中的onStartOtherRemoteActivity方法中自增,之后啟動(dòng)RemoteActivity2,并在2中打印TEST_STATIC的值;
- public static int TEST_STATIC = 21;
- public void onStartOtherRemoteActivity(View view) {
- TEST_STATIC++;
- Log.e(TAG, "onStartOtherRemoteActivity: " + TEST_STATIC);
- startActivity(new Intent(this, RemoteActivity2.class));
- }
- 結(jié)果:
- // RemoteActivity1 log
- E/RemoteActivity1: onStartOtherRemoteActivity: 22
- // RemoteActivity2 log
- E/RemoteActivity2: onCreate: 21
并不相同的數(shù)值說(shuō)明在多進(jìn)程中靜態(tài)變量是失效的,同樣的因?yàn)殪o態(tài)變量帶來(lái)的問(wèn)題是單例模式的失效;
原因就是多進(jìn)程時(shí)Android為其他進(jìn)程分配了一個(gè)新的虛擬機(jī),導(dǎo)致不同的虛擬機(jī)在內(nèi)存上有不同的內(nèi)存地址, 當(dāng)在新的進(jìn)程訪(fǎng)問(wèn)變量時(shí),訪(fǎng)問(wèn)的其實(shí)是這個(gè)類(lèi)在新的虛擬機(jī)中的副本,也就是相當(dāng)于在:remote和.remote中各有一個(gè)RemoteActivity1類(lèi),而.remote訪(fǎng)問(wèn)的那個(gè)副本中的TEST_STATIC是沒(méi)有進(jìn)行自增操作的,所以還是會(huì)打印出21的初始數(shù)值,而在:remote中是自增過(guò)的22;
單例模式也是同樣的解釋?zhuān)?dāng)在另一個(gè)進(jìn)程中訪(fǎng)問(wèn)單例類(lèi)時(shí),在此進(jìn)程中其實(shí)并沒(méi)有進(jìn)行初始化,所以才會(huì)失效;
2、線(xiàn)程同步機(jī)制失效
本質(zhì)上跟靜態(tài)變量類(lèi)似,在一個(gè)進(jìn)程鎖住的是副本的對(duì)象,而在另一個(gè)副本中,內(nèi)存都不同,所以肯定是無(wú)效的;
3、SharedPreferences可靠性下降
SharedPreferences不支持兩個(gè)進(jìn)程同時(shí)去執(zhí)行寫(xiě)操作,否則會(huì)導(dǎo)致一定幾率的數(shù)據(jù)丟失;
SharedPreferences的底層是通過(guò)讀寫(xiě)XML文件實(shí)現(xiàn)的,并發(fā)寫(xiě)很可能導(dǎo)致問(wèn)題,并發(fā)讀寫(xiě)都不能保證不會(huì)出問(wèn)題;
4、Application會(huì)被創(chuàng)建多次
當(dāng)一個(gè)組件跑在一個(gè)新的進(jìn)程中時(shí),系統(tǒng)給新的進(jìn)程分配一個(gè)新的虛擬機(jī),就相當(dāng)于應(yīng)用又一次的重新啟動(dòng),Application作為應(yīng)用基礎(chǔ)肯定也會(huì)被重新創(chuàng)建;
新建Application類(lèi),繼承自Application,并在onCreate方法中輸出當(dāng)前進(jìn)程的PID:
- public class LApplication extends Application {
- private static final String TAG = "LApplication";
- @Override
- public void onCreate() {
- super.onCreate();
- Log.e(TAG, "onCreate: " + android.os.Process.myPid());
- }
- }
當(dāng)依次開(kāi)啟進(jìn)程后輸出如下:
- // Main
- E/LApplication: onCreate: 16031
- // RemoteActivity1
- E/LApplication: onCreate: 16127
- // RemoteActivity2
- E/LApplication: onCreate: 16202
Application被創(chuàng)建多次帶來(lái)的問(wèn)題是,有些時(shí)候會(huì)需要在Application中初始化些依賴(lài),但是多進(jìn)程就會(huì)隨著Application的創(chuàng)建而重復(fù)初始化,可以在Application中設(shè)置一些條件跳過(guò)重復(fù)初始化部分;
- // 根據(jù)pid獲取進(jìn)程名
- private String getAppName(int pid) {
- String processName = null;
- ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
- List<ActivityManager.RunningAppProcessInfo> list = am.getRunningAppProcesses();
- for (ActivityManager.RunningAppProcessInfo info : list) {
- try {
- if (info.pid == pid) {
- processName = info.processName;
- return processName;
- }
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- return null;
- }
通過(guò)PID獲取進(jìn)程名,與包名做對(duì)比,只有跟包名一致時(shí)才做一些初始化工作;
總結(jié)
多進(jìn)程實(shí)現(xiàn)今天沒(méi)有講,以后會(huì)講解的;
多進(jìn)程不難的,難的在于要克服困難,戰(zhàn)勝自己;