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