1. 程式人生 > >Android多程序app中Application回撥onCreate()方法被執行多次分析及解決

Android多程序app中Application回撥onCreate()方法被執行多次分析及解決

最近工作中碰到一個問題,在優化app,使用DDMS檢視Application log過程中看到,app啟動了三個程序,一個主程序,兩個附帶的程序。如下圖可看到一個app啟動的三個程序。 


自定義Application回撥方法onCreate()被執行了3次。開始不知是何原因。

相關知識

android:process
從Android開發者文件中的manifest中程序配置android:process可以獲知: 
正常情況下,應用程式的所有元件執行在一個預設的程序名下,因此不需要使用這個屬性。但在需要的情況下,可以通過使用這個屬性來覆蓋預設程序,這樣一個app就跨越多個程序。

如果這個屬性值以冒號(“:”)開始,說明新程序相對於應用程式是一個私有程序,且元件執行在此程序中。若屬性值以小寫字元開始,那麼新程序即是一個全域性程序,元件執行在這個全域性程序中。這也意味著其他應用程式元件可以與此程序進行通訊,減少資源使用。

標籤<application>的android:process屬性可以為整個app內元件設定一個預設的執行程序。

manifest中元件標籤<activity>, <service>, <provider>, <receiver>都支援配置android:process,即每個元件均可以建立執行在自己的一個新程序中。

Application類
我們可以自定義繼承Application類來實現自己的Application,然後在其中的onCreate()方法中進行一定的初始化工作。

若自定義了Application類,那麼需要注意的就是這個類在當app中有多個程序時,每個程序啟動時都會初始化一次Application。在Android中很不幸的就是我們無法為每個新建立的程序來分別建立一個Application類。 


解決方案

第一:getRunningAppProcesses()
每個程序對應一個application,這樣可以通過針對特定程序名,進行相應的初始化工作,避免資源浪費,執行時間消耗。

因為Application的執行時間影響著首個activity,service等的啟動時間。即Application執行時間越長,首個元件(activity)啟動時間越晚,給使用者造成的感覺就是應用啟動速度特別慢。 


可以看出,Application回撥方法onCreate()被執行3次,均執行耗時操作,這樣造成了在點選應用logo後,到看到進入app,首個頁面(Activity)啟動,耗時將近6s,外加處理器速度,在較慢的機器上,這個時間可能更長,甚至超過10s。

目前較多采用的方法既是所提到的根據具體程序來進行相應的初始化工作,核心的獲取對應程序的方法如下:

/**
 * 獲取程序名。
 * 由於app是一個多程序應用,因此每個程序被os建立時,    
 * onCreate()方法均會被執行一次,
 * 進行辨別初始化,針對特定程序進行相應初始化工作,
 * 此方法可以提高一半啟動時間。
 *
 * @param context 上下文環境物件
 *
 * @return 獲取此程序的程序名
 */
private String getProcessName(Context context) {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
    if (runningAppProcesses == null) {
        return "";
    }

    for (ActivityManager.RunningAppProcessInfo runningAppProcess : runningAppProcesses) {
        if (runningAppProcess.pid == android.os.Process.myPid()
                && !TextUtils.isEmpty(runningAppProcess.processName)) {
            return runningAppProcess.processName;
        }
    }
    return "";
}


這樣經過測試,在不同的程序被建立時,進行不同的工作,執行時間可以縮短一半。 


雖然這種方法在某些版本上可以奏效,在但Android 5.0+版本中,由於Google開始收緊對Android底層許可權管理,在趨勢上方法getRunningAppProcesses()將會被斃掉。因為已經在一些版本的環境中,此方法返回null。

第二:UsageStatsManager
使用類UsageStatsManager來獲取執行的apps列表,但是使用這個類需要新增一個許可權PACKAGE_USAGE_STATS,而此許可權是系統許可權,要使用必須到Settings應用中去針對應用進行授權(我們的app使用者肯定不會願意多次一步)。另外,據稱有些OEM廠商已經刪除了此項設定,換言之在Settings中找不到授權入口。

因此這個途徑也就被斃掉了。

最終方案
經過幾天google方案及針對可能的解決方法進行測試,下邊的這個感覺比較靠譜。

這是一個開源專案,專案地址點選這裡。

在我們的Application中整合並測試了該方法。

原始碼中新增一個方法,類似於使用getRunningAppProcesses()方法一樣: 


以下是在三個不同的Android版本進行測試結果: 


可以看到,針對這三個版本是可以達到相關初始化程式碼只執行一次的效果。這樣可以縮短啟動消耗的時間。

更多版本型別測試大家可以自行進行測試。

時間原因,此方案的程式碼及解決方法還沒有來得及跟蹤,有時間在做分析……

這種方案也有限制:

一些版本的系統應用不包括在內,因為他們具有更高級別的SElinux context;

這種方法也不是getRunningAppProcesses()完全的替代,因為它無法給出整合的pkgList,lru和importance資訊;

此庫在7.0開發者預覽版本上是無法起作用的。

下面的一篇文章就來分析下此方案是如何解決讀取程序的——>《Android獲取執行程序解決方案分析》