多程式通訊系列問題

大雄45發表於2021-11-19
導讀 Android中開啟多程式只有一種方法,就是在AndroidManifest.xml中註冊Service、Activity、Receiver、ContentProvider時指定android:process屬性。今天來講解下:多程式通訊方式以及帶來的問題,方便在專案中遇到問題及時的處理;
一、Android中多程式詳解
1、定義
  1. Android的多程式通訊即IPC是指兩個程式之間進行資料交換;
  1. 程式一般指一個執行單元,在PC和移動裝置中指一個程式或應用;
  1. 最簡單的情況下,Android應用中只有一個程式,包含一個執行緒,即主執行緒,也叫作UI執行緒,只能在此執行緒更新操作UI;
  1. 普通情況下是不需要多程式的,但是當應用需要更多的記憶體或者某些特殊的Module或特殊的需求需要執行在多程式條件下;
2、開啟多程式

Android中開啟多程式只有一種方法,就是在AndroidManifest.xml中註冊Service、Activity、Receiver、ContentProvider時指定android:process屬性,例如:

<service 
    android:name=".MyService" 
    android:process=":remote">
</service>
<activity 
    android:name=".MyActivity" 
    android:process="com.test.remote2"> 
</activity>

我們為MyService和MyActivity指定的android:process屬性值有所不同,它們的區別如下:

  1. :remote:以:開頭是一種簡寫,系統會在當前程式名前附件當前包名,完整的程式名為:com.test:remote,同時以:開頭的程式屬於當前應用的私有程式,其它應用的元件不能和它跑在同一程式;
  1. com.test.remote2:這是完整的命名方式,不會附加包名,其它應用如果和該程式的ShareUID、簽名相同,則可以和它跑在同一個程式,實現資料共享;
3、Android中的多程式通訊方式

多程式通訊方式主要有以下幾種,它們之間各有優缺點,可根據使用場景選擇選擇:

  1. AIDL:功能強大,支援程式間一對多的實時併發通訊,並可實現 RPC (遠端過程呼叫);
  1. Messenger:支援一對多的序列實時通訊, AIDL 的簡化版本;
  1. Bundle:四大元件的程式通訊方式,只能傳輸 Bundle 支援的資料型別;
  1. ContentProvider:強大的資料來源訪問支援,主要支援 CRUD 操作,一對多的程式間資料共享,例如我們的應用訪問系統的通訊錄資料;
  1. BroadcastReceiver:即廣播,但只能單向通訊,接收者只能被動的接收訊息;
  1. 檔案共享:在非高併發情況下共享簡單的資料;
  1. Socket:透過網路傳輸資料;

多程式通訊系列問題多程式通訊系列問題

二、 多程式帶來的問題
1、靜態變數失效

在一個Activity中新建一個靜態變數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)); 
} 
結果: 
// RemoteActivity1 log 
E/RemoteActivity1: onStartOtherRemoteActivity: 22 
// RemoteActivity2 log 
E/RemoteActivity2: onCreate: 21

並不相同的數值說明在多程式中靜態變數是失效的,同樣的因為靜態變數帶來的問題是單例模式的失效;

原因就是多程式時Android為其他程式分配了一個新的虛擬機器,導致不同的虛擬機器在記憶體上有不同的記憶體地址, 當在新的程式訪問變數時,訪問的其實是這個類在新的虛擬機器中的副本,也就是相當於在:remote和.remote中各有一個RemoteActivity1類,而.remote訪問的那個副本中的TEST_STATIC是沒有進行自增操作的,所以還是會列印出21的初始數值,而在:remote中是自增過的22;

單例模式也是同樣的解釋,當在另一個程式中訪問單例類時,在此程式中其實並沒有進行初始化,所以才會失效;

2、執行緒同步機制失效

本質上跟靜態變數類似,在一個程式鎖住的是副本的物件,而在另一個副本中,記憶體都不同,所以肯定是無效的;

3、SharedPreferences可靠性下降

SharedPreferences不支援兩個程式同時去執行寫操作,否則會導致一定機率的資料丟失;

SharedPreferences的底層是透過讀寫XML檔案實現的,併發寫很可能導致問題,併發讀寫都不能保證不會出問題;

4、Application會被建立多次

當一個元件跑在一個新的程式中時,系統給新的程式分配一個新的虛擬機器,就相當於應用又一次的重新啟動,Application作為應用基礎肯定也會被重新建立;

新建Application類,繼承自Application,並在onCreate方法中輸出當前程式的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()); 
    } 
}

當依次開啟程式後輸出如下:

// Main 
E/LApplication: onCreate: 16031 
// RemoteActivity1 
E/LApplication: onCreate: 16127 
// RemoteActivity2 
E/LApplication: onCreate: 16202

Application被建立多次帶來的問題是,有些時候會需要在Application中初始化些依賴,但是多程式就會隨著Application的建立而重複初始化,可以在Application中設定一些條件跳過重複初始化部分;

// 根據pid獲取程式名 
private String getAppName(int pid) { 
    String processName = null; 
    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); 
    List 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獲取程式名,與包名做對比,只有跟包名一致時才做一些初始化工作;

原文來自:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2843007/,如需轉載,請註明出處,否則將追究法律責任。

相關文章