Android 性能優化--啟動時間優化

請保持淡定,分析代碼,記住:性能很重要。

啟動時間優化

毫無疑問,應用的啟動速度越快越好。

本文可以幫助你優化應用的啟動時間:首先解釋啟動過程內部機制;然后討論如何分析啟動性能;最后,描述了一些常見的影響啟動時間的問題,并就如何解決這些問題給出一些提示。

第 1 部分:啟動過程內部機制

應用的啟動有三種狀態,不同狀態的啟動時長是不一樣的。三種狀態分別為:冷啟動(cold start),熱啟動(warm start),溫啟動(lukewarm start)。冷啟動即應用從零開始加載運行,而其它則是應用從后臺運行回到前臺運行。建議您始終基于冷啟動的假設進行優化,因為這樣做同樣提升了另兩種啟動狀態的表現。

要使得應用能快速啟動,需要先理解應用以不同狀態啟動時,系統和應用內發生了什么,以及它們如何相互作用。

1) 冷啟動(cold start)

冷啟動指系統沒有該應用的進程,直到開啟應用才創建出應用程序的進程。冷啟動一般指的就是應用在開機后或者系統停止應用后的第一次啟動。因為系統和應用程序在該狀態下啟動需要做更多的工作,所以減少它的啟動時間的挑戰是最大的。

冷啟動初始時,系統完成三個任務:

  • 啟動和加載應用(這里泛指的是應用本身)
  • 創建應用的專屬進程
  • 啟動后立刻顯示啟動窗口(通常是個空白屏)

一旦系統創建了應用的專屬進程,該進程開始創建應用:

  1. 創建應用對象
  2. 啟動主線程(MainThread)
  3. 創建 Main Activity
  4. 加載視圖(Inflating views)
  5. 渲染布局(Laying out)
  6. 執行初始繪制

一旦應用完成了第一次繪制,系統進程就把當前顯示的啟動窗口切換為應用的界面,這時用戶就可以開始使用應用了。

下圖展示了系統和應用的啟動時相互之間的關系:

以上流程中的大部分由系統來控制,需要關注性能問題的地方往往出現在 Application 和 Activity 的創建(onCreate)過程中。

a) 創建 Application

當你的應用啟動,屏幕立即出現的空白屏幕,將在應用完成首屏的繪制時,切換為應用首屏,然后允許用戶開始與應用進行交互。

如果你在應用中重載了 Application.oncreate(),系統將先調用應用的 onCreate()方法。大型的 App 通常會在這里做大量的通用組件、SDK 的初始化操作。

然后應用程序會生成主線程,也被稱為 UI 線程,并開始創建 Main Activity。

在這之后,系統和應用按各自的 生命周期 運行著。

b) 創建 Activity

應用創建 Activity:

  1. 初始化
  2. 調用構造函數
  3. 調用當前生命周期的回調方法,例如 Activity.onCreate()

通常情況下,onCreate() 方法對加載時間的影響最大,因為它執行了開銷最重的工作:加載、渲染,以及初始化 Activity 所需要的對象,如果布局過于復雜很可能導致嚴重的啟動性能問題。

2) 熱啟動(warm start)

應用程序的熱啟動比冷啟動開銷低。在熱啟動中,系統只是需要把 Activity 切換到前臺運行。如果應用的該 Activity 之前駐留在內存中,那么應用程序就不用重新初始化對象和渲染布局。

但是,如果由于響應了低內存事件,例如在 onTrimMemory() 方法中清除了資源對象,那么這些對象就需要在熱啟動時重新創建。

熱啟動與冷啟動的顯示情況是一致的:系統進程顯示空白屏幕,直到應用程序已經完成 Activity 的渲染。即如果從內存中直接切換,則不會顯示空白屏幕,如果內存內容被清除,將顯示空白屏幕等待渲染完成。

3) 溫啟動(lukewarm start)

溫啟動為冷啟動的過程操作的子集:這代表開銷比熱啟動稍大。以下這些情況可以認為是溫啟動:

  1. 用戶退出應用,但隨后重新啟動它。應用的進程還在運行,但應用必須從新從 onCreate()開始創建 Activity。

  2. 系統從內存中清除了應用(非用戶主動),然后用戶重新啟動它。進程和 Activity 需要重新啟動,但 onCreate()將接收到保存狀態的 Bundle。事實上,savedInstanceState 的保存在用戶未主動銷毀 Activity 時系統就會調用。

第 2 部分:剖析啟動性能

為了正確評估啟動時的表現,你需要跟蹤應用啟動到顯示需要多長時間。下圖展示了應用初始顯示的時間和完全顯示的時間的定義。

1) 初始顯示的時間

a) Displayed

從 Android 4.4(API 19) 開始,logcat 的輸出包括了一行 Displayed 的值。這個值表示了應用啟動進程到 Activity 完成屏幕繪制經過的時間。經過的時間包括以下事件,按順序為:

  1. 啟動進程
  2. 初始化對象
  3. 創建和初始化 Activity
  4. 布局渲染
  5. 完成第一次繪制

報告的日志行看起來類似于下面的例子:

I/ActivityManager: Displayed com.android.contacts/.activities.PeopleActivity: +612ms

如果您在終端使用 logcat,可以直接找到這一行,當然,為了方便需要使用 grep 進行查找。而如果使用 Android Studio 查看,你必須在你的 logcat 視圖中禁用過濾器,因為這是系統打的日志而不是應用本身。一旦您完成了過濾器設置,就可以輕松地搜索到該行查看時間。下圖 展示了如何禁用過濾器,及 logcat 窗口顯示 Displayed 時間的例子。

Displayed 時間顯示的是到第一次繪制的時候,它并不包括不被布局文件及初始化對象所引用的資源的加載時間,因為這個加載是一個內部過程,不阻塞應用初始內容的顯示。

b) ADB Shell Activity Manager

你也可以使用 ADB Shell Activity Manager 測量啟動到顯示的時間。下面是一個例子:

adb shell am start -S -W com.android.contacts/.activities.PeopleActivity 
-c android.intent.category.LAUNCHER 
-a android.intent.action.MAIN

你的終端窗口就像顯示 Displayed 一樣地顯示如下內容:

Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.contacts/.activities.PeopleActivity }
Status: ok
Activity: com.android.contacts/.activities.PeopleActivity
ThisTime: 701
TotalTime: 701
WaitTime: 718
Complete

通過可選參數-c 和-a 可以指定 Intent 的 <category> 和 <action>。

2) 完全展示的時間

a) reportFullyDrawn()

你可以使用 reportFullyDrawn() 方法來測量應用啟動到所有資源和視圖層次結構的完整顯示之間所經過的時間,這在應用使用延遲加載的情況下是很有用的。

在延遲加載時,應用在初始的繪圖之后,異步加載資源,然后更新視圖。如果由于延遲加載,應用的初始顯示并不包括所有的資源,你可能會考慮將所有的資源和視圖的完全加載和顯示作為一個單獨的指標。例如,您的用戶界面可能已經完成了文本的加載,但又必須從網絡獲取圖像。

為了解決這個問題,你可以手動調用 reportFullyDrawn() ,讓系統知道你的 Activity 完成了它的延遲加載。當您使用此方法,logcat 將顯示出從創建應用對象到調用 reportFullyDrawn() 方法的時間。下面是 logcat 的輸出的例子:

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

b) screenrecord

還有一種測量啟動時間的方法值得一提,因為這種方法雖然繁瑣但可以很直觀查看起止位置的時間,那就是通過 screenrecord 命令。該命令可以直接錄制屏幕,通過以下命令啟動:

adb shell screenrecord --bugreport /sdcard/launch.mp4

在手機上操作,點擊 App,等待其顯示,必要時可以多等待一會兒,然后使用 Ctrl + c 停止命令,就得到了想要的視頻了。使用命令導出視頻:

adb pull /sdcard/launch.mp4

接著就可以使用一個能逐幀查看的視頻播放器——例如 QuickTime 播放器來查看視頻,一般地,認為 App 的圖標高亮時為計時的起點,記錄此刻到你想要的停止的位置之間的時間就可以了。簡單來說,就是錄制一個視頻,使用逐幀查看的視頻播放器方便地記錄下你想查看的任意起止時刻。

如果你發現啟動時間比預期要慢,你可以嘗試著找出啟動過程中的瓶頸。

3) 識別性能瓶頸

有兩個很好的方法可以用來來定位問題:Method Tracer 工具和 Systrace 工具。

a) Method Tracer 工具

在 Android Studio 的 CPU Monitor 欄中,提供了 Method Tracer 工具。

首先需要啟動要監控的應用,在 Android Studio 下方的 Android Monitor 中選擇該應用的進程(圖中長方框位置),就可以看到 Memory Monitor / CPU Monitor / Network Monitor 都開始工作起來。

如果要使用 Method Trace 功能,只需要點擊 Start Method Tracing(圖中小方框),在手機上進行操作之后,再次點擊它停止 Method Trace,稍等片刻就能在工程的 captures 文件夾中找到 .trace 文件了。

由以上流程可以知道對于冷啟動而言是無法在正確的時間啟動該工具以獲得日志信息的。這種情況下可以在代碼中合適的位置,例如 onCreate() 和 onResume() 中,添加 android.os.Debug.startMethodTracing() 和 android.os.Debug.stopMethodTracing() 方法來生成 trace 文件,該文件生成在 sdcard 根目錄下或者應用的讀寫目錄中。

Note: 運行 Method Trace 將明顯地影響運行應用的效果。 Method Trace 應該用來了解程序的流程及方法的運行時間比例,其計時時間不可直接作為應用性能的表現。

使用 Android Studio 打開 trace 文件,如果是 CPU Monitor 中生成了 trace 文件,Android Studio 會自動打開它,你將得到如下形式的圖片:

列名 具體含義
Name 方法名
Invocation Count 方法調用次數
Inclusive Time (microseconds) 該方法及其調用的子方法的耗時
Exclusive Time (microseconds) 該方法(不包含調用的子方法)的耗時

圖表的 x 坐標可以選擇 Wall Clock Time 或者 Thread Time ,其中前者表示方法調用到返回結果真實的 CPU 時間,后者表示線程調度的時間,如果線程不連續執行,那么被中斷的時間將被排除,所以將小于前者的統計。

也可以使用 DDMS 打開 trace 文件,其展示的視圖如下所示:

各列名稱及其含義與 Android Studio 的圖示基本類似。

還可以使用 dmtracedump 工具解析生成 html 文件如下圖(dmtracedump 可以生成圖片,但往往混亂到看不出順序,有興趣的可以自行查閱相關資料):

從以上三種方式展示的 trace 文件結果來看,結果中包含了 JDK 函數,第三方庫函數,以及 Android SDK 中函數,如果想僅分析應用中的方法調用順序信息,可以根據 trace 文件過濾出當前應用下的方法信息。目前 GitHub 上有一個 Windows 平臺下的 分析應用方法耗時的 swing 工具 ,其使用方法很簡單:

  • 將 sdk\platform-tools 下的 dmtracedump 添加到系統環境變量
  • 基于 jdk 1.8 環境運行 Method-trace-analysis.jar
  • 直接導入 .trace 文件,一鍵分析(注意:trace 文件路徑不要包含空格)

該工具的思路基于: 一個能讓你了解所有函數調用順序以及函數耗時的 Android 庫(無需侵入式代碼) ,該庫核心就是 2 個 build.gradle 中的 task 基于 dmtracedump 工具對 trace 文件進行解析、過濾。

Method Trace Tool 得到了良好的展示效果,如圖:

以上 trace 文件的幾種展示方式可以讓你了解到關于應用中方法的調用順序及耗時信息(注意:該耗時信息不代表真正使用場景下的耗時),基于以上信息可以分析出一個方法或者一個環節是否成為性能瓶頸。

b) Systrace 工具

另一個跟蹤的方法就是 Systrace 的使用了。

 

 

來自:http://blog.csdn.net/lgz_ei/article/details/70041663

 

 本文由用戶 ewha1196 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!