android进程自动启动时间,如何统计Android App启动时间
隨著App的邏輯不斷龐大,一不注意就會將耗時的操作放置在應用啟動過程之中,導致應用啟動速度越來越慢,用戶體驗也越來越差。優化啟動速度是幾乎所有大型App應用開發者需要考慮的問題。優化啟動速度之前首先需要準確測量App啟動時間,這樣有利于我們更準確可量化地看出優化效果,也可以指導我們進行持續優化。轉載請注明出處:Lawrence_Shen
同時可以參考2019年的性能分析文章:Android性能分析&啟動優化
- 使用命令行方式
使用命令行方式統計多次啟動某個Activity的平均用時可以在shell中執行如下指令:
adb shell am start -S -R 10 -W com.example.app/.MainActivity
其中-S表示每次啟動前先強行停止,-R表示重復測試次數。每一次的輸出如下所示信息。
Stopping: com.example.app
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.app/.MainActivity }
Status: ok
Activity: com.example.app/.MainActivity
ThisTime: 1059
TotalTime: 1059
WaitTime: 1073
Complete
其中TotalTime代表當前Activity啟動時間,將多次TotalTime加起來求平均即可得到啟動這個Activity的時間。
缺點
應用的啟動過程往往不只一個Activity,有可能是先進入一個啟動頁,然后再從啟動頁打開真正的首頁。某些情況下還有可能中間經過更多的Activity,這個時候需要將多個Activity的時間加起來。
將多個Activity啟動時間加起來并不完全等于用戶感知的啟動時間。例如在啟動頁可能是先等待某些初始化完成或者某些動畫播放完畢后再進入首頁。使用命令行統計的方式只是計算了Activity的啟動以及初始化時間,并不能體現這種等待任務的時間。
沒有在AndroidManifest.xml對應的Activity聲明中指定或者屬性沒有android:exported="true"的Activity不能使用這種命令行的形式計算啟動時間。
-思考更準確的方式
以上基于命令行的方式存在諸多問題,迫使我們思考怎樣才能得到從用戶角度上觀察更準確的啟動時間。在嘗試其他方法之前,我們先定義一下怎樣才是從用戶角度上觀察的啟動時間。
冷啟動、熱啟動(注意不是官方的定義,是我們從用戶角度考慮的定義)
冷啟動時間:冷啟動表示用戶首次打開應用,這時進程還沒創建,包含了Application創建的過程。冷啟動時間指從第一次用戶點擊Launcher中的應用圖標開始,到首頁內容全部展示出來的時間。
熱啟動時間:熱啟動表示用戶在首頁按了返回,首頁Activity已經Destroy,不過Application仍在內存中存在,對應的進程并沒有被殺掉,不包含Application創建過程。熱啟動時間指在Application仍然存在的情況下,從用戶點擊桌面圖標,到首頁內容全部展示出來的時間。
App啟動流程
要優化以及分析啟動時間,需要先了解App的啟動流程。以冷啟動為例子,Application以及Activity的啟動流程如下,參考文章[3][4][5][6]:
app啟動流程
更為直觀和簡單的流程圖參考Colt McAnlis在Android Performance Patterns Season 6中的表述。有興趣的同學可以點擊鏈接看看(Youtube鏈接)。
app啟動流程by Colt McAnlis
從流程圖以及參考Colt McAnlis的Android Performance Patterns[6]得知,在冷啟動的過程中,首先會通過AMS在System進程展示一個Starting Window(通常情況下是個白屏,可以通過設置Application的theme修改),接著AMS會通過Zygote創建應用程序的進程,并通過一系列的步驟后調用Application的attachBaseContext()、onCreate()然后最終調用Activity的onCreate()以及進行View相關的初始化工作。在Activity展示出來后會替換掉之前的Starting Window,這樣啟動過程結束。
如何加log
參考[1]發現在Activity中onWindowFocusChanged()方法是最好的Activity對用戶可見的標志,因此綜合上一節的分析,我們可以考慮在Application的attachBaseContext()方法中開始計算冷啟動計時,然后在真正首頁Activity的onWindowFocusChanged()中停止冷啟動計時,這樣就可以初步得到應用的冷啟動時間。
public void onWindowFocusChanged(boolean hasFocus)
Called when the current android.view.Window of the activity gains or loses focus. This is the best indicator of whether this activity is visible to the user.
為了方便統計,設置一個Util類專門做計時,添加的代碼如下:
/**
* 計時統計工具類
*/
public class TimeUtils {
private static HashMap sCalTimeMap = new HashMap<>();
public static final String COLD_START = "cold_start";
public static final String HOT_START = "hot_start";
public static long sColdStartTime = 0;
/**
* 記錄某個事件的開始時間
* @param key 事件名稱
*/
public static void beginTimeCalculate(String key) {
long currentTime = System.currentTimeMillis();
sCalTimeMap.put(key, currentTime);
}
/**
* 獲取某個事件的運行時間
*
* @param key 事件名稱
* @return 返回某個事件的運行時間,調用這個方法之前沒有調用 {@link #beginTimeCalculate(String)} 則返回-1
*/
public static long getTimeCalculate(String key) {
long currentTime = System.currentTimeMillis();
Long beginTime = sCalTimeMap.get(key);
if (beginTime == null) {
return -1;
} else {
sCalTimeMap.remove(key);
return currentTime - beginTime;
}
}
/**
* 清除某個時間運行時間計時
*
* @param key 事件名稱
*/
public static void clearTimeCalculate(String key) {
sCalTimeMap.remove(key);
}
/**
* 清除啟動時間計時
*/
public static void clearStartTimeCalculate() {
clearTimeCalculate(HOT_START);
clearTimeCalculate(COLD_START);
sColdStartTime = 0;
}
}
然后在Application的attachBaseContext()方法中添加如下代碼:
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
if (/**如果是主進程**/) {
TimeUtils.beginTimeCalculate(TimeUtils.COLD_START);
}
}
在第一個Activity的onCreate()方法中添加如下代碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
calculateStartTime();
....
}
private void calculateStartTime() {
long coldStartTime = TimeUtils.getTimeCalculate(TimeUtils.COLD_START);
// 這里記錄的TimeUtils.coldStartTime是指Application啟動的時間,最終的冷啟動時間等于Application啟動時間+熱啟動時間
TimeUtils.sColdStartTime = coldStartTime > 0 ? coldStartTime : 0;
TimeUtils.beginTimeCalculate(DictTimeUtil.HOT_START);
}
在真正的首頁Activity的 onWindowFocusChanged()方法中添加如下代碼:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus && /**沒有經過廣告或者引導頁**/) {
long hotStartTime = TimeUtils.getTimeCalculate(TimeUtils.HOT_START);
if (TimeUtils.sColdStartTime > 0 && hotStartTime > 0) {
// 真正的冷啟動時間 = Application啟動時間 + 熱啟動時間
long coldStartTime = TimeUtils.sColdStartTime + hotStartTime;
// 過濾掉異常啟動時間
if (coldStartTime < 50000) {
// 上傳冷啟動時間coldStartTime
}
} else if (hotStartTime > 0) {
// 過濾掉異常啟動時間
if (hotStartTime < 30000) {
// 上傳熱啟動時間hotStartTime
}
}
}
}
避免坑的Checklist
上面的分析給了我們初步的加log的起始和結束點,然而在實際的統計中會發現得到的數據有20%左右是不準確的,體現在計時數據非常大,有些甚至會顯示冷啟動時間超過一天。經過分析,在計算啟動計時的時候需要注意一些問題。以下列舉一下添加log時候需要注意的checklist。
應用在啟動過程可能會有廣告(我們的業務是有道詞典),第一次啟動會有引導頁,需要根據業務情況標記在沒有廣告、沒有引導頁的時候才計算。這種情況要注意在非正常啟動的時候忽略啟動時間統計。
由于詞典首頁之前還有幾個Activity,在沒到首頁Activity之前如果過早的返回,會出現冷啟動時間過長的問題。這是因為詞典返回的時候并沒有殺掉進程,而時間統計信息是保存在內存中的,而等下次再進入的時候因為是熱啟動不會重新開始冷啟動計時。這導致了這次熱啟動實際上打log的時候發現有上次冷啟動的開始時間,算成了冷啟動,而且因為啟動時間是上一次的,所以這次冷啟動log的時間比實際時間長。這種情況要注意在首頁Activity之前的其他ActivityonPause()方法中調用TimeUtils.clearStartTimeCalculate();清除計時。
除了正常的啟動流程,應用還有很多可能會導致Application的創建的入口,例如點擊桌面小插件、系統賬號同步、Deep Link跳轉、直接進入設置了的Activity、push達到等。我們需要檢查所有有可能引起Application創建,但是不是正常啟動流程的地方,調用TimeUtils.clearStartTimeCalculate();清除計時,避免引起冷啟動時間計算過長錯誤的問題。
- 使用第三方工具
為了測試啟動的過程中哪些方法比較耗時,我們可以使用Android Studio中集成的Android Monitor提供的Method Tracering或者Systrace。不過在實踐中發現,有另外一個nimbledroid工具使用更加簡便且能更明確指出耗時的地方。上傳了應用之后會自動分析情景如下圖所示。其中會自動檢測出首頁的Activity并且給出冷啟動的啟動情況。
情景分析
點擊進入Cold Startup的情景可以看到主要耗時的方法如下圖。
情景詳細耗時統計
至于為什么nimbledroid會知道那個是我們首頁的Activity,官網上解析如下:
We use a heuristic to tell when an app finishes startup by detecting when (1) the main Activity has been displayed and (2) things like animated progress bars in the main Activity have stopped. Based on our experiments, this heuristic works in most cases.
點擊進入某個方法,可以看到這個方法具體是由于調用了哪個子方法導致了耗時的問題。
耗時方法詳細
通過nimbledroid這個工具,我們可以比較輕松地發現一些比較明顯的問題,并可以指導我們進行啟動優化。同時nimbledroid還支持Memory Leaks、網絡監測以及結果分享等一些功能,更多的功能有待讀者繼續發現。
- 后記
統計和分析啟動時間有利于指導我們優化啟動時間。以上介紹了有道詞典在進行啟動優化中的分析過程。通過詳細了解Android應用啟動的流程,進行準確的log記錄,并且結合第三方工具,我們最終得到準確的啟動時間統計數據以及啟動優化的一些頭緒。具體優化的方法參加下一篇文章《如何優化Androd App啟動速度》。
- 參考
總結
以上是生活随笔為你收集整理的android进程自动启动时间,如何统计Android App启动时间的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android lottie字体json
- 下一篇: android 短信时间排序,仿ipho