這可能是最好的效能優化教程(二)

nanchen2251發表於2017-09-13

這可能是最好的效能優化教程系列專欄
這可能是最好的效能優化教程(一)
這可能是最好的效能優化教程(二)
這可能是最好的效能優化教程(三)

前言

上篇寫了 ArrayMap 和 UI 效能優化,這篇我想和大家聊聊每個人都關心的 APP 啟動,參見 Android 效能優化典範

正文

提高 APP 的啟動速度對我們意義深遠,很顯然,APP 的啟動時間越短,使用它的使用者越有耐心等待開啟這個 APP 進行使用。反之,啟動時間太長,使用者則有可能還沒等到 APP 開啟就已經切換到其他 APP 了。

程式啟動過程中那些複雜錯誤的操作很有可能導致嚴重的效能問題。Android 系統會根據使用者的操作行為調整程式的顯示策略,用來提高程式的顯示效能。例如,一旦使用者點選桌面圖示,Android 系統會立即顯示一個啟動視窗,這個視窗會一直保持顯示直到畫面中的元素成功載入並繪製完第一幀。這種行為常見於程式的冷啟動,或者程式的熱啟動場景(程式從後臺被喚起或者從其他 APP 介面切換回來)。

那麼關鍵的問題是,使用者很可能會因為從啟動視窗到顯示畫面的過程耗時過長而感到厭煩,從而導致使用者沒有來得及等程式啟動完畢就切換到其他 APP 了。更嚴重的是,如果啟動時間過長,可能導致程式出現 ANR。我們應該避免出現這兩種糟糕的情況

從技術角度來說,當使用者點選桌面圖示開始,系統會立即為這個 APP 建立獨立的專屬程式,然後顯示啟動視窗,直到 APP 在自己的程式裡面完成了程式的建立以及主執行緒完成了 Activity 的初始化顯示操作,再然後系統程式就會把啟動視窗替換成 APP 的顯示視窗。

上述流程裡面的絕大多數步驟都是由系統控制的,一般來說不會出現什麼問題,可是對於啟動速度,我們能夠控制並且需要特別關注的地方主要有三處:

  • Activity 的 onCreate 流程,特別是UI的佈局與渲染操作,如果佈局過於複雜很可能導致嚴重的啟動效能問題。
  • Application 的 onCreate 流程,對於大型的 APP 來說,通常會在這裡做大量的通用元件的初始化操作。
  • 目前有部分 APP 會提供自定義的啟動視窗,這裡可以做成品牌宣傳介面或者是給使用者提供一種程式已經啟動的視覺效果。

在正式著手解決問題之前,我們需要掌握一套正確測量評估啟動效能的方法。所幸的是,Android 系統有提供一些工具來幫助我們定位問題。

有趣的啟動時長定位

display time

從 Android KitKat 版本開始,Logcat 中會輸出從程式啟動到某個 Activity 顯示到畫面上所花費的時間。這個方法比較適合測量程式的啟動時間。

reportFullyDrawn()

我們通常來說會使用非同步懶載入的方式來提升程式畫面的顯示速度,這通常會導致的一個問題是,程式畫面已經顯示,可是內容卻還在載入中。為了衡量這些非同步載入資源所耗費的時間,我們可以在非同步載入完畢之後呼叫 activity.reportFullyDrawn() 方法來告訴系統此時的狀態,以便獲取整個載入的耗時。

Method Tracing

前面兩個方法提供了啟動耗時的總時間,可是卻無法提供具體的耗時細節。為了獲取具體的耗時分佈情況,我們可以使用 Method Tracing 工具來進行詳細的測量。

Systrace

我們可以在 onCreate() 方法裡面新增 trace.beginSection()trace.endSection() 方法來宣告需要跟蹤的起止位置,系統會幫忙統計中間經歷過的函式呼叫耗時,並輸出報表。

如果優化 APP 啟動速度?

提升 Activity 的建立速度

提升 Activity 的建立速度是優化 APP 啟動速度的首要關注目標。從桌面點選 APP 圖示啟動應用開始,程式會顯示一個啟動視窗等待 Activity 的建立載入完畢再進行顯示。在 Activity 的建立載入過程中,會執行很多的操作,例如設定頁面的主題,初始化頁面的佈局,載入圖片,獲取網路資料,讀寫 Preference 等等。


上述操作的任何一個環節出現效能問題都可能導致畫面不能及時顯示,影響了程式的啟動速度。上一個段落我們介紹了使用 Method Tracing 來發現那些耗時佔比相對較多的方法。假設我們發現某個方法執行時間過長,接下去就可以使用 Systrace 來幫忙定位到底是什麼原因導致那個方法執行時間過長。

除了使用工具進行具體定位分析效能問題之外,以下兩點經驗可以幫助我們對 Activity 啟動做效能優化:

  • 優化佈局耗時:一個佈局層級越深,裡面包含需要載入的元素越多,就會耗費更多的初始化時間。關於佈局效能的優化,這裡就不展開描述了!
  • 非同步延遲載入:一開始只初始化最需要的佈局,非同步載入圖片,非立即需要的元件可以做延遲載入。

別讓 Application 初始化不必要的東西

在 Application 初始化的地方做太多繁重的事情是可能導致嚴重啟動效能問題的元凶之一。Application 裡面的初始化操作不結束,其他任意的程式操作都無法進行。

有時候,我們會一股腦的把絕大多數全域性元件的初始化操作都放在 Application 的 onCreate() 裡面,但其實很多元件是需要做區隊對待的,有些可以做延遲載入,有些可以放到其他的地方做初始化操作,特別需要留意包含 Disk IO 操作,網路訪問等嚴重耗時的任務,他們會嚴重阻塞程式的啟動。


優化這些問題的解決方案是做延遲載入,可以在 Application 裡面做延遲載入,也可以把一些初始化的操作延遲到元件真正被呼叫到的時候再做載入。

恰當地使用閃屏

啟動閃屏不僅僅可以作為品牌宣傳頁,還能夠減輕使用者對啟動耗時的感知,但是如果使用不恰當,將適得其反。前面介紹過當點選桌面圖示啟動 APP 的時候,程式會顯示一個啟動視窗,一直到頁面的渲染載入完畢。如果程式的啟動速度足夠快,我們看的閃屏視窗停留顯示的時間則會很短,但是當程式啟動速度偏慢的時候,這個啟動閃屏可以一定程度上減輕使用者等待的焦慮感,避免使用者過於輕易的關閉應用。

目前大多數開發者都會通過設定啟動視窗主題的方式來替換系統預設的啟動視窗,通過這種方式只是使用『障眼法』弱化了使用者對啟動時間的感知,但本質上並沒有對啟動速度做什麼優化。也有些 APP 通過關閉啟動視窗屬性 android:windowDisablePreview 的方式來直接移除系統預設的啟動視窗,但是這樣的弊端是使用者從點選桌面圖示到真的看到實際頁面的這段時間當中,畫面沒有任何變化,這樣的使用者體驗是十分糟糕的!

對於啟動閃屏,正確的使用方法是自定義一張圖片,把這張圖片通過設定主題的方式顯示為啟動閃屏,程式碼執行到主頁面的 onCreate() 的時候設定為程式正常的主題。

後記

本篇我們根據胡凱老師總結,解決了啟動速度響應,下期我們將帶來記憶體管理章節。
如果想第一時間收到更新資訊的可以關注我的簡書:簡書地址
你也可以選擇關注我的公眾號:nanchen

相關文章