1. 程式人生 > >Android多程序Process開發總結-優點與缺陷-(個人註釋版,非絕對原創)

Android多程序Process開發總結-優點與缺陷-(個人註釋版,非絕對原創)

1、背景

我公司產品,一共有三個程序,其中主程序一個、子程序一個、推送程序一個

 

Androiod多程序

為何使用多程序,有啥好處?

 推送業務為何都要獨立程序,這裡涉及到的一個知識就是程序保活技術,推送程序只要不掛掉,那麼推送保證沒有問題

a、不會影響到主業務的程式碼的穩定執行

b、還不佔用主業務程序的記憶體

c、自己獨立的生命週期,這樣主業務程序就算被幹死,也不會影響自己

d、推送程序就算崩潰了,主業務程序不受影響嘛,上面已經說過了,同第a點

e、獨立程序有作業系統開闢都獨立記憶體控制元件,不會和其他程序產生佔用記憶體,影響記憶體分配問題,同第b點

f、獨立程序的啟動和退出可以完全不依賴使用者對應用的使用,可以獨立啟動、退出,也可以不會因主業務程序退出而被結束了程序,同第c點

 什麼情況下,我們會去想到使用多程序呢?
 就我個人開發實踐中就多次使用了Android多程序機制,如專案中的推送業務開發,提出要求如下:
  - 不能影響主業務的程式碼穩定執行 
  - 不能佔用主業務的程序記憶體 
  - 不受主業務程序生命週期影響,獨立存在和執行
    要滿足這3個需求下,不由就會想到在應用內開闢一個新程序單獨給推送業務使用,因為其特點明顯:
  - 獨立程序執行出現了崩潰和異常而退出並不會影響其他程序執行
  - 獨立程序意味著有系統開闢的獨立記憶體空間,不會和其他程序產生佔用記憶體,影響其記憶體分配問題
  - 獨立程序的啟動和退出可以完全不依賴使用者對應用的使用,可以獨立啟動、退出,也可以不會因應用退出而結束了程序。
   可以看出明顯滿足上述要求,那如何開闢呢。Android系統中使用多程序配置還是挺簡單的,只要在Manifest中元件(如Service、Activity)直接配置android:process=“”屬性即可完成配置。 Android下的多程序使用雖然很簡單,但是如果不注意細節就很容易出現一些細節上的問題。

開發細節

android:process=”:XXX”與android:process=”XXX”區別

  android:process=":xxx"與android:process="XXX"不僅僅是用來定義當前程序的名字,一般情況各元件的程序名均為當前應用的包名。那有":"開頭意味著:
  - 1、程序名字是在當前程序名(即包名)下追加命名,如當前包名為com.terminal.a,那麼第一種情況下的程序名就是com.terminal.a:xxx而第二種就直接以"xxx"來命名;
  - 2、":"表示當前新程序為主程序(程序名為包名)的私有子程序,其他應用的元件不可以和它跑在同一個程序中。而後者則是全域性的程序,其他應用通過設定相同的ShareUID可以和它跑在同一個程序。

獨立一個程序,和獨立一個子程序的區別?

a、元件的屬性上,android:process=":xxx"與android:process="xxx"的區別

-表面上是一個 冒號 的區別

-首先元件一般不用顯式的設定android:process這個屬性的,沒有設定的,當然元件是在預設的程序裡

-首先看下帶有 冒號 的  比如我的程序名是 com.wyw.qq, 那麼有一個元件是 android:process=":rong", 那麼這個元件所在的程序名就是com.wyw.qq:rong

-再看下沒有 冒號的 比如我的程序名仍是 com.wyw.qq,那麼有一個元件是 android:process="com.rong.push", 那麼這個元件所在的程序名就是 com.rong.push,即寫了什麼就是什麼

 

-帶有冒號的程序,其實代表的是主程序的私有程序,比如com.wyw.qq:rong就是com.wyw.qq的私有程序

-直接先談下私有程序的特點:其他程序的元件是不能執行在私有程序中的

 

-沒有冒號的程序,其實代表的是完全獨立的一個程序,比如上面com.rong.push,它就是完全獨立的一個程序,其他程序的元件也可以執行在這個程序裡,前提條件是:設定了相同的shareUID,這樣其他程序的元件就可以和這個獨立程序執行在一起了,這裡UID是指,Linux下的UID

 

如何判斷是當前應用主程序,方法如下

 private boolean isLocalAppProcess(String packName) {
    int myPid = Process.myPid();
    ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.RunningAppProcessInfo myProcess = null; //RunningAppProcessInfo應該是Task list有的程序吧,不然被啟動的程序也就算了,怎麼著最次也得是後臺程序
    List runningProcesses = activityManager.getRunningAppProcesses();
    if (runningProcesses != null) {
        Iterator var11 = runningProcesses.iterator();
        while (var11.hasNext()) {
            ActivityManager.RunningAppProcessInfo process = (ActivityManager.RunningAppProcessInfo) var11.next();
            Log.i("chuan", "process=" + process.processName + " pid=" + process.pid);
            if (process.pid == myPid) {
                myProcess = process;
                break;
            }
        }
    }

    if (myProcess == null) {
        Log.i("chuan", "Could not find running process for %d" + myPid);
        return false;
    } else {
        return myProcess.processName.equals(packName);
    }
}

思路就是遍歷所有的程序,如果有與你要求的程序名一樣的話,就算找到當前應用的主程序嘛

 

多應用同名程序過濾,保活一個,思路自理

 假如我們開發的是一個Sdk,需要給多個應用使用,如果多個應用都嵌入了我們的Sdk並且都裝在了同一個手機上,如果同時讓多個相同業務的程序一起在後臺嗨著,這樣不僅給使用者帶來頭疼和影響(想想那麼多的推廣訊息和廣告同時間而來,以及嚴重耗電),也給Sdk後端帶來負載壓力,故此我們只能獨活一個程序,讓其他應用相同業務程序休眠。
 如何實現,這裡只講思路,不便公開程式碼。不論用什麼手段喚起程序,系統fork新程序以及對程序的管理都是有序有規則,不會亂象叢生,因為系統不僅fork程序,還要維護管理其生命週期,對每個程序的生命週期管理,當記憶體不足,還會根據優先順序來殺死程序。但它們都有一個統一編制的Id,這個是有序的,不會重複,故我們根據遍歷的方式來查詢這些程序,並且只保留一個程序。

 

程序間通訊方法

 這塊知識點詳細展開很多,後面有計劃專門寫個AIDL程序間的通訊。程序間實現通訊的方法可以是:
 -  通過Intent/Bundle傳遞資料,如在4大元件間,通過Intent向service傳遞資料
 -  通過檔案快取方式,一個程序寫過之後,另一個程序來讀,但不能同時
 -  通過Socket方式,例如系統中程序服務也有用如此方式
 -  通過AIDL來實現程序間通訊,可以做到多端連線,非同步通訊,避免阻塞 
 - 通過四大元件實現程序間通訊:廣播、Activity、Service、ContentProvider

 

開發中陷阱和坑

主程序的Application多次建立,這裡的邏輯要理解,元件雖然是新的程序中建立,但是主程序肯定要先啟動起來嘛

 當元件要在新程序中被外部啟用或者喚醒,系統會首先fork該元件當前所在的主程序,並建立該程序的Application物件並初始化,然後再啟動即將要在新程序中建立的元件,這個和啟動應用的流程一樣。如上文的Push程序啟動的時候,就會導致主程序的Aplication先會被初始化一次。這個情況下就會出現一個應用的Aplication物件被多次建立並初始化,若應用配置定義自己的Aplication,並在oncreate()函式中進行了自己業務相關的邏輯,常見的是應用啟動統計,這個時候就會由於其他程序如後臺程序啟動而導致了應用啟動次數統計增加而且實際應用根本沒有啟動(指的是使用者雖然沒有看到,但是主程序已經在記憶體建立了,只是開啟一個Activity而已)這樣就導致資料不準。這個時候怎麼辦呢?就可以利用上文中"如何判斷是當前應用程序,方法如下"方法 在Application的onCreate()初始化中增加判斷程式碼,當不是的當前應用程序直接return,不要走下面邏輯程式碼
public void onCreate() {
    super.onCreate();
    if (!isLocalAppProcess(getPackageName())) {
        return;
    }
    *******
    應用內相關邏輯
    ******
}

Static靜態變數快取失效以及單例項

在實際開發中常用static變數來作為單例項,實際就是用來快取唯一的物件,而在跨程序時候,如果希望引用其他程序中已經初始化過的static變數,會發現直接獲取的是null。這個是因為程序間記憶體區域是獨立的,系統程序沙箱模式。導致根本就訪問不了另一個程序的變數記憶體地址,更獲取不到其值。

 

SharePreferenc通訊失效

 從上午可以多程序之間記憶體是不共享,那如何通訊呢?剛開始的時候,嘗試用過檔案儲存啊,用SharePreference來儲存,但失效並沒有用。可能好奇為何啊,又不是上述的記憶體變數訪問方式,怎麼不可以通過檔案來訪問。這個其實由於SharePreference實現機制導致的,sharePreference每次操作讀寫並非實時向檔案同步,而是向程序中的快取的Map物件的sharePrefenceImpl物件同步,每個程序維護的是當前的map物件,而非檔案,只有當程序退出之後,才會寫入檔案,系統這樣設計主要是為了效能著想,如果每次都進行IO操作必定阻塞,故視為輕量級的檔案儲存之一吧,所以還是不能通過sharePrefence來通訊,如果應用程序沒有退出,那麼獲取到的sharePrefence僅僅是檔案中上一次儲存的始值。
 同時其他檔案通訊方式也要注意,不能同時進行讀寫操作。否則這就可能造成資源的競爭訪問,導致諸如資料庫損壞、資料丟失等。一般在多執行緒的情況下我們有鎖機制控制資源的共享,但是在多程序中比較難,雖然有檔案鎖、排隊等機制,但是在Android裡很難實現。