一、什么是啟動時長?
1、啟動時長一般包括三種場景,分別是:新裝包的首次啟動時長,冷啟動時長、熱啟動時長
冷啟動 和 熱啟動 :
(1)冷啟動:當啟動應用時,后台沒有該程序的進程,此時啟動的話系統會分配一個新的進程給應用。
(2)熱啟動:程序的進程依然存在,啟動時通過已有進程啟動進入到Activity顯示頁面的,就是熱啟動,或者從Android官網來看我們獲取到的其實是溫啟動時長,就是Activity不存在的情況。
(3)新裝包的啟動時長:
新裝包的啟動時長,預估是最長的,並且在5.0以下及5.0以上的Android系統上的表現不同,因為:Android 5.0和更高版本使用名為ART的運行時,它原生支持從APK文件加載多個DEX文件。在應用安裝時,它會執行預編譯,掃描classes(..N).dex文件然后將其編譯成單個.oat文件用於執行。而Android5.0以下的系統不支持,只能在程序點擊啟動之后,進行多個dex文件的加載,如果是在Android5.0以下的機型上獲取時長,就能明顯看到新裝包啟動時長要比其他啟動時長都要長。
其中冷啟動的過程如下圖所示:

系統開始啟動Activity的時間,就是從start u0這個時間看的,准備啟動這個Activity的時間,就是這條日志前面打印出來的這個時間:
12-14 16:45:51.483 I/ActivityManager( 1370): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.ganji.android/.control.LaunchActivity bnds=[571,684][763,876] (has extras)} from uid 10021 on display 0
之后該Activity的第一幀展示出來的時候,會打印出Display的日志信息,如:
12-14 16:45:52.023 I/ActivityManager( 1370): Displayed com.ganji.android/.control.LaunchActivity: +521ms
從start u0到display第一幀的這個時間 包括了從啟動進程到第一次布局與繪制的所有時間。這基本上是你需要知道的主要時間。它不包含用戶點擊app圖標然后系統開始准備啟動activity的時間,這是ok的
2、關於我們想要獲取的三種場景的啟動時長
(1)三種場景是啟動的三種典型場景,這三種場景並沒有包含將緩存和數據清除掉的情況;其中用戶經歷次數最多的是 “冷啟動時長” 和“ 熱啟動時長”
(2)新裝包的首次啟動時長,一般情況下是最多的,在Android5.0以下系統上從APK文件加載多個dex文件的過程也包含在內。
冷啟動時長居第二位,因為殺進程之后內存中的緩存會丟失,很多數據需要重新請求,並且需要重新啟動進程;
熱啟動時長是最短的。
(3)我們想要獲取啟動時長的場景:
都是從初始的LaunchActivity頁面進入到下一個的可操作的頁面的時間;
比如以趕集網為例,新裝包首次啟動之后會跳轉到選擇城市的頁面,我們需要獲取的時長就是從 LaunchActivity的頁面進入到SelectcityActivity的頁面
因為用戶一般會用到的場景是:選擇城市進入到主頁面,下次再啟動都是直接進入到主頁面,所以編寫了uiautomator的腳本,能夠通過UItest的方式選擇城市;之后再分別獲取 “冷啟動時長” 和 “熱啟動時長”,即殺進程和正常退出程序的啟動時長
二、如何獲取?
1、因為通過logcat打印日志過濾ActivityManager的方式來獲取啟動時間,能夠對其他APP也適用,因此最終確定用這種方式。
其中以趕集生活為例,新裝包首次啟動時長的獲取,需要獲取的是:從LaunchActivity的啟動,到Displayed出來SelectCityActivity,剛開始用的是Displayed的后面的這個time的時長,但是跟開發確認之后,他說這個時間比用戶的實際感知過程的時間還少一些,最終確定的是:
獲取從start u0 LaunchActivity的時間到Display可操作的Activity的時間,拿到前面的時間節點之后,然后進行計算
以趕集網為例,三種場景下在 小米 4 + Android4.4.4的機型下,通過logcat抓到的日志如下:
新裝包首次啟動從Launch界面進入到選擇城市的界面:
12-12 17:08:51.101 I/ActivityManager( 1191): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ganji.android/.control.LaunchActivity} from pid 5656
12-12 17:08:58.141 I/ActivityManager( 1191): Displayed com.ganji.android/.control.LaunchActivity: +6s803ms
12-12 17:08:59.041 I/ActivityManager( 1191): START u0 {flg=0x200000 cmp=com.ganji.android/.control.BetterCityActivity (has extras)} from pid 5668
12-12 17:08:59.711 I/ActivityManager( 1191): Displayed com.ganji.android/.control.BetterCityActivity: +654ms
熱啟動時長:
12-12 17:11:23.571 I/ActivityManager( 1191): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ganji.android/.control.LaunchActivity} from pid 6369
12-12 17:11:24.021 I/ActivityManager( 1191): Displayed com.ganji.android/.control.LaunchActivity: +264ms
12-12 17:11:25.311 I/ActivityManager( 1191): START u0 {cmp=com.ganji.android/.control.MainActivity} from pid 5668
12-12 17:11:27.311 I/ActivityManager( 1191): Displayed com.ganji.android/.control.MainActivity: +1s989ms
冷啟動時長:
12-12 17:13:16.981 I/ActivityManager( 1191): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ganji.android/.control.LaunchActivity} from pid 6625
12-12 17:13:18.231 I/ActivityManager( 1191): Displayed com.ganji.android/.control.LaunchActivity: +1s237ms
12-12 17:13:19.231 I/ActivityManager( 1191): START u0 {cmp=com.ganji.android/.control.MainActivity} from pid 6637
12-12 17:13:22.811 I/ActivityManager( 1191): Displayed com.ganji.android/.control.MainActivity: +3s556ms
三種場景下,都需要從start u0 cmp = LaunchActivity的時間開始,到Displayed 下一個Activity的時間結束
2、當然,也可以通過錄屏的方式,錄屏的命令是:
adb shell screenrecord --bugreport /sdcard/launch.mp4
之后按Ctrl+C結束,然后將視頻文件導出,之后使用可按幀播放的播放器即可。
使用播放器播放的時候,可以從用戶點擊屏幕發白發亮的那一刻算是開始時間,這個時間可以精確到ms,之后結束的話,就以你到達的頁面,並且可以等待異步加載的都完成為止。
這個與 1 中的方法相比,獲取的時間是會更長一些,因為包含了點擊之后有了用戶的touch操作,之后 系統響應,再到系統開始響應啟動進程,創建Activity,加載視圖,度量並進行Activity的整體顯示的過程
並且通過logcat的方式,只能用在Activity從A跳轉到B的這種情況,如果是Activity不變化,只是頁面數據在刷新,logcat就無法通過ActivityManager打印出日志。
最終方案確定:
最終根據我們與開發的溝通,及通過這兩種方式的對比,也為了后續能夠方便通過程序寫成自動化的形式,選擇了第一種,並且增加了通過uiautomator完成城市選擇和點擊back鍵正常退出app的UI自動化的case來支持自動化實現。
三、最后確定的方案,方案的實施和工具實現
針對以上需求:
1、方便獲取競品的對比數據,需要在同一機型上執行,並且最好執行多次,選擇一個平均值
2、方便方案的通用性,以及自動化實現
通過程序實現,主要包含以下過程:
1、自動裝包
2、啟動logcat,清除logcat的緩存,執行 adb logcat -v time -s ActivityManager ,並將輸出內容輸出到某個文件
3、啟動應用程序,通過adb shell am start LaunchActivity的方式,獲取新裝包的啟動時間;之后通過uiautomator執行UI操作,完成熱啟動時間的獲取,之后通過adb shell am force-stop pkg包名的方式殺進程,再啟動完成冷啟動時間的獲取
3、將日志文件從手機中pull出來
4、對日志文件進行分析,因為前面都增加了-v time 的選項,log中能夠打印出具體到ms的時間,可以根據你設置的規則,直接獲取某條日志前面的時間
5、獲取到開始時間和結束時間之后,用結束時間減去開始時間即可
備注:
參考資料————————————
APP的安裝流程:
http://www.cnblogs.com/sunshine-anycall/p/3544345.html
Android APP的啟動過程:
http://blog.csdn.net/freshui/article/details/8695463
http://www.androidchina.net/3851.html
APK預編譯提取Odex:http://blog.csdn.net/huangyabin001/article/details/46973625
Android MultiDex實踐:https://gold.xitu.io/entry/5705b2712e958a0057a5f735
Android應用打破65K方法數限制:http://www.infoq.com/cn/news/2014/11/android-multidex
Android developer的官網針對Launch-Time Performance的內容如下:https://developer.android.com/topic/performance/launch-time.html
