編輯:關於Android編程
前言
作為 Android 開發者,想必多多少少要接觸啟動速度優化相關的事情,當用戶越來越多,產品的功能也隨著迭代越來越多,App 逐漸變得臃腫是一件很常見的現象,甚至可以說是不可避免的現象,隨之而來的工作就是優化 App 性能,其中最主要的一項就是啟動速度優化。但本文的主角並不是啟動速度優化,而是啟動時間統計。
一、啟動類型
工欲善其事,必先利其器。想要優化 App 的啟動速度,必須有准確衡量啟動時間的方法,否則優化完之後效果怎樣,自己都不知道,說出去別人也不信服不是。在做 App 啟動時間統計之前,當然必須弄明白有哪些啟動類型,每種啟動類型的特點。通常來說,在安卓中應用的啟動方式分為以下幾種:
1、冷啟動:當啟動應用時,後台沒有該應用的進程,這時系統會重新創建一個新的進程分配給該應用,這個啟動方式就是冷啟動。冷啟動因為系統會重新創建一個新的進程分配給它,所以會先創建和初始化 Application
類,再創建和初始化 MainActivity
類,最後顯示在界面上。
2、熱啟動:當啟動應用時,後台已有該應用的進程(例:按back鍵、home鍵,應用雖然會退出,但是該應用的進程是依然會保留在後台,可進入任務列表查看),所以在已有進程的情況下,這種啟動會從已有的進程中來啟動應用,這個方式叫熱啟動。熱啟動因為會從已有的進程中來啟動,所以熱啟動就不會走 Application
這步了,而是直接走 MainActivity
,所以熱啟動的過程不必創建和初始化 Application
,因為一個應用從新進程的創建到進程的銷毀,Application
只會初始化一次。
3、首次啟動:首次啟動嚴格來說也是冷啟動,之所以把首次啟動單獨列出來,一般來說,首次啟動時間會比非首次啟動要久,首次啟動會做一些系統初始化工作,如緩存目錄的生產,數據庫的建立,SharedPreference的初始化,如果存在多 dex 和插件的情況下,首次啟動會有一些特殊需要處理的邏輯,而且對啟動速度有很大的影響,所以首次啟動的速度非常重要,畢竟影響用戶對 App 的第一映像。
二、本地啟動時間的統計方式
如果是本地調試的話,統計啟動時間還是很簡單的,通過命令行方式即可:
adb shell am start -w packagename/activity
輸出的結果類似於:
$ adb shell am start -W com.speed.test/com.speed.test.HomeActivity Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.speed.test/.HomeActivity } Status: ok Activity: com.speed.test/.HomeActivity ThisTime: 496 TotalTime: 496 WaitTime: 503 Complete
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
最終調用 Instrumentation
的 execStartActivity
來啟動應用。
3、Instrumentation
調用 ActivityManagerProxy
(ActivityManagerService 在應用進程的一個代理對象) 對象的 startActivity
方法啟動 Activity
。
4、到目前為止所有過程都在 Launcher
進程裡面執行,接下來 ActivityManagerProxy
對象跨進程調用 ActivityManagerService
(運行在 system_server 進程)的 startActivity
方法啟動應用。
5、ActivityManagerService
的 startActivity
方法經過一系列調用,最後調用 zygoteSendArgsAndGetResult
通過 socket
發送給 zygote
進程,zygote
進程會孵化出新的應用進程。
6、zygote 進程孵化出新的應用進程後,會執行 ActivityThread
類的 main
方法。在該方法裡會先准備好 Looper
和消息隊列,然後調用 attach
方法將應用進程綁定到 ActivityManagerService
,然後進入 loop
循環,不斷地讀取消息隊列裡的消息,並分發消息。
7、ActivityManagerService
保存應用進程的一個代理對象,然後 ActivityManagerService
通過代理對象通知應用進程創建入口 Activity
的實例,並執行它的生命周期函數。
總結過程就是:用戶在 Launcher
程序裡點擊應用圖標時,會通知 ActivityManagerService
啟動應用的入口 Activity
, ActivityManagerService
發現這個應用還未啟動,則會通知 Zygote
進程孵化出應用進程,然後在這個應用進程裡執行 ActivityThread
的 main
方法。應用進程接下來通知 ActivityManagerService
應用進程已啟動,ActivityManagerService
保存應用進程的一個代理對象,這樣 ActivityManagerService
可以通過這個代理對象控制應用進程,然後 ActivityManagerService
通知應用進程創建入口 Activity 的實例,並執行它的生命周期函數。
生命周期函數執行流程
上面的啟動流程是 Android 提供的機制,作為開發者我們需要清楚或者至少了解其中的過程和原理,但我們並不能在這過程中做什麼文章,我們能做的恰恰是從上述過程中最後一步開始,即 ActivityManagerService
通過代理對象通知應用進程創建入口 Activity
的實例,並執行它的生命周期函數開始,我們的啟動時間統計以及啟動速度優化也是從這裡開始。下面是 Main Activity 的啟動流程:
-> Application 構造函數 -> Application.attachBaseContext() -> Application.onCreate() -> Activity 構造函數 -> Activity.setTheme() -> Activity.onCreate() -> Activity.onStart -> Activity.onResume -> Activity.onAttachedToWindow -> Activity.onWindowFocusChanged
如果打 Log 記錄 App 的啟動時間,那麼至少要記錄兩個點,一個起始時間點,一個結束時間點。
起始時間點
起始時間點比較容易記錄:如果記錄冷啟動啟動時間一般可以在 Application.attachBaseContext()
開始的位置記錄起始時間點,因為在這之前 Context
還沒有初始化,一般也干不了什麼事情,當然這個是要視具體情況來定,其實只要保證在 App 的具體業務邏輯開始執行之前記錄起始時間點即可。如果記錄熱啟動啟動時間點可以在 Activity.onRestart()
中記錄起始時間點。
結束時間點
結束時間點理論上要選在 App 顯示出第一屏界面的時候,但是在什麼位置 App 顯示出第一屏界面呢?網上很多文章說在 Activity
的 onResume
方法執行完成之後,Activity
就對用戶可見了,實際上並不是,一個 Activity
走完onCreate onStart onResume
這幾個生命周期之後,只是完成了應用自身的一些配置,比如 Activity
主題設置 window 屬性的設置 View
樹的建立,但是其實後面還需要各個 View
執行 measure layout draw
等。所以在 OnResume
中記錄結束時間點的 Log 並不准確,大家可以注意一下上面流程中最後一個函數 Activity.onWindowFocusChanged
,下面是它的注釋:
/** *Called when the current {@link Window} of the activity gains or loses * focus. This is the best indicator of whether this activity is visible * to the user. The default implementation clears the key tracking * state, so should always be called. ... */
通過注釋我們可以看到,這個函數是判斷 activity
是否可見的最佳位置,所以我們可以在 Activity.onWindowFocusChanged
記錄應用啟動的結束時間點,不過需要注意的是該函數,在 Activity
焦點發生變化時就會觸發,所以要做好判斷,去掉不需要的情況。
總結
以上就是關於安卓(Android)開發之統計App啟動時間的全部內容,本文的內容小編覺得還是很重要的,還是那句話:工欲善其事,必先利其器,准備工作做的充分,做事自然有理有據。希望本文的內容對大家有所幫助。
我們知道,Android系統基於Linux實現。針對傳統Linux系統,NSA開發了一套安全機制SELinux,用來加強安全性。然而,由於Android系統有著獨特的用戶
實現功能:歌曲下載完成後通知主界面更新本地音樂除了下面說明的一個問題,還有一些BUG有待修復,後續博文將會繼續更新//DownloadDialogFragment回傳的是
簡介:基本概念:Service是Android四大組件之一,運行在後台執行耗時操作並且不提供用戶界面。其他組件(如Activity)可以通過startService啟動該
在android中一共有 多種事件,每種事件都有自己相對應的處理機制 如以下幾種 1 單擊事件 View.OnClickListener pub