Android 開發之 App 啟動時間統計

發表於2016-08-15

引言

作為 Android 開發者,想必多多少少要接觸啟動速度優化相關的事情,當使用者越來越多,產品的功能也隨著迭代越來越多,App 逐漸變得臃腫是一件很常見的現象,甚至可以說是不可避免的現象,隨之而來的工作就是優化 App 效能,其中最主要的一項就是啟動速度優化。但本文的主角並不是啟動速度優化,而是啟動時間統計。

一 啟動型別

工欲善其事,必先利其器。想要優化 App 的啟動速度,必須有準確衡量啟動時間的方法,否則優化完之後效果怎樣,自己都不知道,說出去別人也不信服不是。在做 App 啟動時間統計之前,當然必須弄明白有哪些啟動型別,每種啟動型別的特點。通常來說,在安卓中應用的啟動方式分為以下幾種:

  1. 冷啟動:當啟動應用時,後臺沒有該應用的程式,這時系統會重新建立一個新的程式分配給該應用,這個啟動方式就是冷啟動。冷啟動因為系統會重新建立一個新的程式分配給它,所以會先建立和初始化 Application 類,再建立和初始化 MainActivity 類,最後顯示在介面上。
  2. 熱啟動:當啟動應用時,後臺已有該應用的程式(例:按back鍵、home鍵,應用雖然會退出,但是該應用的程式是依然會保留在後臺,可進入任務列表檢視),所以在已有程式的情況下,這種啟動會從已有的程式中來啟動應用,這個方式叫熱啟動。熱啟動因為會從已有的程式中來啟動,所以熱啟動就不會走 Application 這步了,而是直接走 MainActivity,所以熱啟動的過程不必建立和初始化 Application,因為一個應用從新程式的建立到程式的銷燬,Application 只會初始化一次。
  3. 首次啟動:首次啟動嚴格來說也是冷啟動,之所以把首次啟動單獨列出來,一般來說,首次啟動時間會比非首次啟動要久,首次啟動會做一些系統初始化工作,如快取目錄的生產,資料庫的建立,SharedPreference的初始化,如果存在多 dex 和外掛的情況下,首次啟動會有一些特殊需要處理的邏輯,而且對啟動速度有很大的影響,所以首次啟動的速度非常重要,畢竟影響使用者對 App 的第一映像。

二 本地啟動時間的統計方式

如果是本地除錯的話,統計啟動時間還是很簡單的,通過命令列方式即可:

輸出的結果類似於:

  • WaitTime 返回從 startActivity 到應用第一幀完全顯示這段時間. 就是總的耗時,包括前一個應用 Activity pause 的時間和新應用啟動的時間;
  • ThisTime 表示一連串啟動 Activity 的最後一個 Activity 的啟動耗時;
  • TotalTime 表示新應用啟動的耗時,包括新程式的啟動和 Activity 的啟動,但不包括前一個應用Activity pause的耗時。

開發者一般只要關心 TotalTime 即可,這個時間才是自己應用真正啟動的耗時。

三 線上啟動時間的統計方式

當 App 發到線上之後,想要統計 App 在使用者手機上的啟動速度,就不能通過命令列的方式進行統計了,基本上都是通過打 Log 的方式將啟動時間傳送上來。那麼在什麼位置加啟動時間統計的 Log 就尤為重要,Log 新增的位置直接決定啟動時間統計的是否準確,同樣也會影響啟動速度優化效果的判斷。要想找到合適準確的位置記錄啟動時間的 Log,就需要了解應用的啟動流程,和各個生命週期函式的呼叫順序。下面來分析下到底在什麼位置打 Log 記錄啟動時間比較合適。

應用的主要啟動流程

關於 App 啟動流程的文章很多,文章底部有一些啟動流程相關的參考文章,這裡只列出大致流程如下:

  1. 通過 Launcher 啟動應用時,點選應用圖示後,Launcher 呼叫 startActivity 啟動應用。
  2. Launcher Activity 最終呼叫 InstrumentationexecStartActivity 來啟動應用。
  3. Instrumentation 呼叫 ActivityManagerProxy (ActivityManagerService 在應用程式的一個代理物件) 物件的 startActivity 方法啟動 Activity
  4. 到目前為止所有過程都在 Launcher 程式裡面執行,接下來 ActivityManagerProxy 物件跨程式呼叫 ActivityManagerService (執行在 system_server 程式)的 startActivity 方法啟動應用。
  5. ActivityManagerServicestartActivity 方法經過一系列呼叫,最後呼叫 zygoteSendArgsAndGetResult 通過 socket 傳送給 zygote 程式,zygote 程式會孵化出新的應用程式。
  6. zygote 程式孵化出新的應用程式後,會執行 ActivityThread 類的 main 方法。在該方法裡會先準備好 Looper 和訊息佇列,然後呼叫 attach 方法將應用程式繫結到 ActivityManagerService,然後進入 loop 迴圈,不斷地讀取訊息佇列裡的訊息,並分發訊息。
  7. ActivityManagerService 儲存應用程式的一個代理物件,然後 ActivityManagerService 通過代理物件通知應用程式建立入口 Activity 的例項,並執行它的生命週期函式。

總結過程就是:使用者在 Launcher 程式裡點選應用圖示時,會通知 ActivityManagerService 啟動應用的入口 ActivityActivityManagerService 發現這個應用還未啟動,則會通知 Zygote 程式孵化出應用程式,然後在這個應用程式裡執行 ActivityThreadmain 方法。應用程式接下來通知 ActivityManagerService 應用程式已啟動,ActivityManagerService 儲存應用程式的一個代理物件,這樣 ActivityManagerService 可以通過這個代理物件控制應用程式,然後 ActivityManagerService 通知應用程式建立入口 Activity 的例項,並執行它的生命週期函式。

生命週期函式執行流程

上面的啟動流程是 Android 提供的機制,作為開發者我們需要清楚或者至少了解其中的過程和原理,但我們並不能在這過程中做什麼文章,我們能做的恰恰是從上述過程中最後一步開始,即 ActivityManagerService 通過代理物件通知應用程式建立入口 Activity 的例項,並執行它的生命週期函式開始,我們的啟動時間統計以及啟動速度優化也是從這裡開始。下面是 Main Activity 的啟動流程:

如果打 Log 記錄 App 的啟動時間,那麼至少要記錄兩個點,一個起始時間點,一個結束時間點。

起始時間點

起始時間點比較容易記錄:如果記錄冷啟動啟動時間一般可以在 Application.attachBaseContext() 開始的位置記錄起始時間點,因為在這之前 Context 還沒有初始化,一般也幹不了什麼事情,當然這個是要視具體情況來定,其實只要保證在 App 的具體業務邏輯開始執行之前記錄起始時間點即可。如果記錄熱啟動啟動時間點可以在 Activity.onRestart() 中記錄起始時間點

結束時間點

結束時間點理論上要選在 App 顯示出第一屏介面的時候,但是在什麼位置 App 顯示出第一屏介面呢?網上很多文章說在 ActivityonResume 方法執行完成之後,Activity 就對使用者可見了,實際上並不是,一個 Activity 走完onCreate onStart onResume 這幾個生命週期之後,只是完成了應用自身的一些配置,比如 Activity 主題設定 window 屬性的設定 View 樹的建立,但是其實後面還需要各個 View 執行 measure layout draw等。所以在 OnResume 中記錄結束時間點的 Log 並不準確,大家可以注意一下上面流程中最後一個函式 Activity.onWindowFocusChanged,下面是它的註釋:

通過註釋我們可以看到,這個函式是判斷 activity 是否可見的最佳位置,所以我們可以在 Activity.onWindowFocusChanged 記錄應用啟動的結束時間點,不過需要注意的是該函式,在 Activity 焦點發生變化時就會觸發,所以要做好判斷,去掉不需要的情況。

總結

花了很多篇幅介紹啟動時間統計,我覺得還是有必要的,還是那句話:工欲善其事,必先利其器,準備工作做的充分,做事自然有理有據。

參考:
https://www.zhihu.com/question/35487841
http://blog.adisonhyh.com/blog/2015/03/27/androidxiang-mu-xing-neng-you-hua-zhi-qi-dong-shi-jian/
http://www.woaitqs.cc/android/2016/06/21/activity-service.html
http://blog.csdn.net/zhaokaiqiang1992/article/details/49428287
http://www.cloudchou.com/android/post-805.html
http://www.jianshu.com/p/72059201b10a

相關文章