性能優化系列(六)啓動性能優化

文章首發「Android波斯灣」公衆號,更新地址:https://github.com/jeanboydev/Android-ReadTheFuckingSourceCode

提高程序的啓動速度意義重大,很顯然,啓動時間越短,用戶才越有耐心等待打開這個 App 進行使用,反之啓動時間越長,用戶則越有可能來不及等到 App 打開就已經切換到其他 App 了。

程序啓動過程中的那些複雜錯誤的操作很可能導致嚴重的性能問題。Android 系統會根據用戶的操作行爲調整程序的顯示策略,用來提高程序的顯示性能。

例如,一旦用戶點擊桌面圖標,Android 系統會立即顯示一個啓動窗口,這個窗口會一直保持顯示直到畫面中的元素成功加載並繪製完第一幀。

這種行爲常見於程序的冷啓動,或者程序的熱啓動場景(程序從後臺被喚起或者從其他 App 界面切換回來)。

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

啓動方式

Android 應用的啓動方式分爲三種:冷啓動、暖啓動、熱啓動,不同的啓動方式決定了應用 UI 對用戶可見所需要花費的時間長短。

顧名思義,冷啓動消耗的時間最長。基於冷啓動方式的優化工作也是最考驗產品用戶體驗的地方。談及優化之前,下面我們來看看這三種啓動方式的應用場景,以及啓動過程中系統都做了些什麼工作。

冷啓動

在安卓系統中,系統爲每個運行的應用至少分配一個進程 (多進程應用申請多個進程) 。從進程角度上講,冷啓動就是在啓動應用前,系統中沒有該應用的任何進程信息 (包括 Activity、Service 等) 。

所以,冷啓動產生的場景就很容易理解了,比如設備開機後應用的第一次啓動,系統殺掉應用進程 (如:系統內存吃緊引發的 kill 和用戶主動產生的 kill)後的再次啓動等。那麼自然這種方式下,應用的啓動時間最長,因爲相比另外兩種啓動方式,系統和我們的應用要做的工作最多。

應用發生冷啓動時,系統有三件任務要做:

  • 開始加載並啓動應用;
  • 應用啓動後,顯示一個空白的啓動窗口;
  • 創建應用進程信息;

系統創建應用進程後,應用就要做下面這些事情:

  • 初始化應用中的對象(比如 Application 中的工作);
  • 啓動主線程(UI 線程);
  • 創建第一個 Activity;
  • 加載內容視圖(Inflating);
  • 計算視圖在屏幕上的位置排版(Laying out);
  • 繪製視圖(draw)。

只有當應用完成第一次繪製,系統當前展示的空白背景纔會消失,纔會被 Activity 的內容視圖替換掉。也就是這個時候,用戶才能和我們的應用開始交互。

下圖展示了冷啓動過程系統和應用的一個工作時間流:

在這裏插入圖片描述

這其中有兩個 creation 工作,分別爲 Application 和 Activity creation。從圖中看出,他們均在 View 繪製展示之前。所以,在應用自定義的 Application 類和 第一個 Activity 類中,onCreate() 方法做的事情越多,冷啓動消耗的時間越長。

暖啓動

當應用中的 Activities 被銷燬,但在內存中常駐時,應用的啓動方式就會變爲暖啓動。

相比冷啓動,暖啓動過程減少了對象初始化、佈局加載等工作,啓動時間更短。但啓動時,系統依然會展示一個空白背景,直到第一個 Activity 的內容呈現爲止。

熱啓動

相比暖啓動,熱啓動時應用做的工作更少,啓動時間更短。熱啓動產生的場景很多,常見如:用戶使用返回鍵退出應用,然後馬上又重新啓動應用。

啓動時間

從技術角度來說,當用戶點擊桌面圖標開始,系統會立即爲這個 App 創建獨立的專屬進程,然後顯示啓動窗口,直到 App 在自己的進程裏面完成了程序的創建以及主線程完成了 Activity 的初始化顯示操作,再然後系統進程就會把啓動窗口替換成 App 的顯示窗口。

在這裏插入圖片描述

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

  • Application

Application 的 onCreate 流程,對於大型的 App 來說,通常會在這裏做大量的通用組件的初始化操作。

  • Activity

Activity 的 onCreate 流程,特別是 UI 的佈局與渲染操作,如果佈局過於複雜很可能導致嚴重的啓動性能問題。

  • 閃屏

目前有部分 APP 會提供自定義的啓動窗口,這裏可以做成品牌宣傳界面或者是給用戶提供一種程序已經啓動的視覺效果。

Display Time

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

從 Android 4.4(API 19)開始,Logcat 自動幫我們打印出應用的啓動時間。這個時間值從應用啓動(創建進程)開始計算,到完成視圖的第一次繪製(即 Activity 內容對用戶可見)爲止。如:

display time

統計啓動時間

如果是本地調試的話,統計啓動時間還是很簡單的,通過命令行方式即可:

$ adb shell am start -w <包名>/activity

輸出的結果類似於:

$ adb shell am start -W com.jeanboy.app.test/com.jeanboy.app.test.HomeActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.jeanboy.app.test/.HomeActivity }
Status: ok
Activity: com.jeanboy.app.test/.HomeActivity
ThisTime: 496
TotalTime: 496
WaitTime: 503
Complete
  • WaitTime

返回從 startActivity 到應用第一幀完全顯示這段時間。就是總的耗時,包括前一個應用 Activity pause 的時間和新應用啓動的時間;

  • ThisTime

表示一連串啓動 Activity 的最後一個 Activity 的啓動耗時;

  • TotalTime

表示新應用啓動的耗時,包括新進程的啓動和 Activity 的啓動,但不包括前一個應用 Activity pause 的耗時。

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

當 App 發到線上之後,想要統計 App 在用戶手機上的啓動速度,就不能通過命令行的方式進行統計了,基本上都是通過打 Log 的方式將啓動時間發送上來。

優化 Application

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

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

在這裏插入圖片描述

優化這些問題的解決方案是做延遲加載,可以在 Application 裏面做延遲加載,也可以把一些初始化的操作延遲到組件真正被調用到的時候再做加載。

優化 Activity

提升 Activity 的創建速度是優化 App 啓動速度的首要關注目標。從桌面點擊 App 圖標啓動應用開始,程序會顯示一個啓動窗口等待 Activity 的創建加載完畢再進行顯示。

在 Activity 的創建加載過程中,會執行很多的操作,例如設置頁面的主題,初始化頁面的佈局,加載圖片,獲取網絡數據,讀寫 Preference 等等。

在這裏插入圖片描述

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

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

  • 優化佈局耗時

一個佈局層級越深,裏面包含需要加載的元素越多,就會耗費更多的初始化時間。關於佈局性能的優化,這裏就不展開描述了!

  • 異步延遲加載

一開始只初始化最需要的佈局,異步加載圖片,非立即需要的組件可以做延遲加載。

優化閃屏

啓動閃屏不僅僅可以作爲品牌宣傳頁,還能夠減輕用戶對啓動耗時的感知,但是如果使用不恰當,將適得其反。

前面介紹過當點擊桌面圖標啓動 App 的時候,程序會顯示一個啓動窗口,一直到頁面的渲染加載完畢。如果程序的啓動速度足夠快,我們看的閃屏窗口停留顯示的時間則會很短,但是當程序啓動速度偏慢的時候,這個啓動閃屏可以一定程度上減輕用戶等待的焦慮感,避免用戶過於輕易的關閉應用。

目前大多數開發者都會通過設置啓動窗口主題的方式來替換系統默認的啓動窗口,通過這種方式只是使用「障眼法」弱化了用戶對啓動時間的感知,但本質上並沒有對啓動速度做什麼優化。

也有些 App 通過關閉啓動窗口屬性 android:windowDisablePreview 的方式來直接移除系統默認的啓動窗口,但是這樣的弊端是用戶從點擊桌面圖標到真的看到實際頁面的這段時間當中,畫面沒有任何變化,這樣的用戶體驗是十分糟糕的!

在這裏插入圖片描述

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

在這裏插入圖片描述

我的 GitHub

github.com/jeanboydev

技術交流羣

歡迎加入技術交流羣,來一起交流學習。

QQ 技術交流羣
微信技術交流羣

我的公衆號

歡迎關注我的公衆號,分享各種技術乾貨,各種學習資料,職業發展和行業動態。

Android 波斯灣

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章