前言
?啟動速度是用戶對于App好用與否的第一印象,如果用戶在打開一個頁面等待時間超過8S,那么用戶將放棄等待,因此啟動速度優化是對于一款App來說是重要的環節之一。
啟動分類
?Android啟動方式分為冷啟動、熱啟動、溫啟動,一般情況下我們做啟動優化主要是針對冷啟動做的。首先我們可以先通過一些方式去獲得啟動的時間,時間測量有ADB命令、手動打點、AOP等方式,每一種方式都有各自的特點和使用場景。
冷啟動流程(耗時最長)
launcher->Ipc->Process.start->ActivityThread->bindApplication->Lifecycle->ViewRootImpl
熱啟動流程(耗時最短)
后臺->前臺
1 ADB命令統計時間
?Google提供的啟動時間文章App startup time
1.1 先安裝app到虛擬機
1.2 使用adb命令
adb shell am start -W 包名/activiti絕對路徑
例如:adb shell am start -W com.example.demo/.MainActivity打開應用的MainActivity界面
- 注意:-W 使用的是大寫的W
1.3 查看結果
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]
cmp=com.example.myapplication/.MainActivity }
Status: ok
Activity: com.example.myapplication/.MainActivity
ThisTime: 944
TotalTime: 944
WaitTime: 955
Complete
- ThisTime : 最后一個Activity啟動耗時
- TotalTime : 所有Activity啟動耗時
- WaitTime : AMS啟動Activity的總耗時
?一般情況下ThisTime和TotalTime是相等的。
缺點
?使用ADB命令只能測試某個Activity的打開到初始化完成的時間,但是在真實的場景中有時候并不是只需要啟動一個Activity,例如有可能先打開一個啟動頁SplashActivity經過一段時間的動畫,再打開真正的首頁,這才是用戶真正等待的時間,使用ADB命令并不能測量出來。因此使用ADB命令來所測量出的時間并不嚴謹。
?沒有在AndroidManifest.xml
對應的Activity聲明中指定為啟動頁
或者屬性沒有android:exported="true"
的Activity不能使用這種命令行的形式計算啟動時間。
2 手動打點
?手動打點就是在啟動開始時和結束時進行埋點操作,然后計算二者的差值。
?首先要在Application中的onattachBaseContext
方法進行開始啟動時間埋點,這是我們所能接觸到的最早啟動的時間。
class MyApplication : Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
CalculatTime.startRecord()
}
override fun onCreate() {
super.onCreate()
}
}
?然后在第一次展示數據時,或Feed第一條展示時結束埋點。
observer = view.getViewTreeObserver();
//注冊觀察者,監聽變化
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if(observer.isAlive()){
observer.removeOnPreDrawListener(this);
}
CalculateTime.INSTANCE.endRecord("MainActivity");
return true;
}
});
?注意:這里會存在一個誤區,在onWindowFocusChanged進行結束時間的記錄,這是不合適的。
?我們不僅可以計算用戶真正看到第一幀所消耗的時間,也可以通過手動打點的方式計算時間,其中包括第三方SDK(友盟、百度統計等等)或者其它需要第一時間初始化的框架。
object CalculateTime {
private var startTime: Long = 0
fun startRecord() {
startTime = SystemClock.currentThreadTimeMillis()
}
fun endRecord(tag: String) {
Log.d(tag, "${SystemClock.currentThreadTimeMillis() - startTime}")
}
}
總結
?啟動時間測量有兩種方式,一種是ADB命令,另一種是手動打點的方式。手動打點要避免誤區,在第一條數據展示時才算打點結束。