這個主題可協助您找出並修正應用程式的主要效能問題。
主要效能問題
有許多問題會導致應用程式效能低落,但以下列舉一些應用程式應注意的常見情況:
- 捲動資源浪費
- 「資源浪費」這個術語是用來描述在系統無法依要求步調 (例如 60 Hz 或更高) 及時建構和提供到特定畫面時出現的視覺中斷。資源浪費在捲動時最顯而易見,當理應是流暢的動畫流程出現中斷時,由於應用程式轉譯內容的時間比系統畫面的持續時間還長,因此移動時會暫停一或多個畫面。
- 應用程式應鎖定 90 Hz 的重新整理頻率。傳統的轉譯率為 60 Hz,但許多新型的裝置在使用者互動 (例如捲動) 時都是在 90 Hz 模式下運作,而有些裝置甚至支援更高的頻率 (最高 120 Hz)。
- 如要查看裝置在特定時間的重新整理頻率,請在「Debugging」(偵錯)區段中使用「Developer Option」(開發人員選項) >「Show refresh rate」(顯示重新整理頻率) 來啟用重疊。
-
- 啟動延遲時間是指輕觸應用程式圖示、通知或其他進入點,以及使用者資料顯示在畫面上所需的時間。
您的目標應專注在應用程式中建立下列兩個啟動目標:
冷啟動時間低於 500 毫秒:當系統記憶體中不存在啟動的應用程式時,就會出現「冷啟動」。這是應用程式自重新啟動或使用者或系統終止應用程式程序後首次啟動時會出現的情形。
相較之下,如果應用程式已在背景執行,就會發生「暖啟動」。冷啟動需要系統處理大部分作業,因為系統必須從儲存空間載入各種項目並初始化應用程式。目的是讓冷啟動的目標不超過 500 毫秒。
因此,P95/P99 延遲時間非常接近中位數延遲時間。 當應用程式有時要花冗長的時間啟動時,將消磨使用者的信任感。在應用程式啟動的關鍵路徑中,IPC 和不必要的 I/O 可能會遭遇鎖定內容的情況,並造成這些不一致的問題。
轉換不順暢
- 這些問題會在互動期間發生,例如切換分頁或載入新活動。這些類型的轉換應含有流暢的動畫,且不應出現延遲或視覺效果閃爍的情況。
電源效率不佳
- 這麼做會消耗電池電力,而執行不必要的作業也會降低電池續航力。
記憶體配置 (透過程式碼建立新物件) 可能會導致系統作業負荷增加。原因在於,這些配置作業不僅須透過 Android 執行階段來完成,稍後釋出這些物件 (「垃圾收集」) 也需要時間與心力。配置和收集作業都比以前更快,效率更高,特別是暫存物件。因此,過往的指南是盡可能避免配置物件,現在的建議則是落實對您應用程式和架構最有意義的事情;考量到 ART 的能力,在程式碼無法維護的風險下進行配置節約並非正確的選擇。
不過,由於您還要努力,因此請記住,如果您要在內部迴圈中配置多數物件,就可能會引起效能問題。
找出問題
識別及救濟效能問題的建議工作流程如下:
- 識別需要檢查的關鍵使用者旅程。其中可能包括:
- 常見的啟動流程,包括啟動器和通知。
- 使用者捲動資料的任何畫面。
- 畫面轉換。
- 長時間執行的流程,例如導航或播放音樂。
- 使用偵錯工具檢查這些流程發生的情況:
- Systrace 或 Perfetto:可讓您透過精確的時序資料掌握整部裝置發生的情形。
- 記憶體分析器:可讓您查看堆積上的記憶體配置情形。
- Simpleperf:查看特定函式呼叫在特定時間範圍內佔用最多 CPU 的火焰圖。當您發現某些項目在 Systrace 中花費的時間較長,但您不知道原因為何時,Simpleperf 會提供其他資訊。
對個別測試執行手動偵錯作業時,務必瞭解及偵錯這些效能問題。「無法」透過分析匯總資料取代上述步驟。不過,在自動化測試和欄位中設定指標收集也很重要,可讓您瞭解使用者實際看到的內容,並找出發生迴歸問題的時機:
- 啟動流程
- 欄位指標:Play 管理中心啟動時間
- 研究室測試 Jetpack Macrobenchmark: 啟動
- 資源浪費
- 欄位指標
- Play 管理中心畫面重點:請注意,您無法在 Play 管理中心將指標限縮至特定使用者歷程,因為回報的所有內容都是應用程式整體資源浪費情形。
- 使用
FrameMetricsAggregator
執行自訂測量。您可以在特定工作流程中,使用FrameMetricsAggregator
記錄資源浪費指標。
- 研究室測試
- Jetpack Macrobenchmark: 捲動
- Macrobenchmark 可利用囊括單一使用者歷程的
dumpsys gfxinfo
指令來收集影格時間。這是瞭解特定使用者歷程中資源浪費變化的合理方法。RenderTime
指標會特別醒目顯示畫面的繪製時間,而此項目比指定迴歸或改善項時的畫面資源浪費計數來得重要。
- 欄位指標
設定您的應用程式進行效能分析
正確設定是應用程式取得準確、可重複且可行基準的關鍵。請盡可能使用接近實際工作環境的系統,同時抑制雜訊來源。下列各節說明您在準備測試設定時可以採用的 APK 與特定系統步驟,其中有些步驟僅適用於特定用途。
追蹤點
應用程式可以透過自訂追蹤記錄事件來檢測程式碼。
擷取追蹤記錄時,每區段中的每個追蹤記錄都會產生少許額外負荷 (約 5μs),因此請避免在每種方法中導入。只追蹤大型工作區塊 (>0.1 毫秒) 可提供重大的瓶頸深入分析。
APK 注意事項
注意:請勿測量偵錯版本的效能。
偵錯變化版本可協助您進行疑難排解,以及為堆疊樣本進行符號化處理,但會對效能造成嚴重的非線性影響。在執行 Android 10 (API 級別 29) 以上版本的裝置,可以使用資訊清單中的 profileable android:shell="true"
來啟用發布子版本的剖析功能。
使用實際執行等級的程式碼縮減設定。視應用程式使用的資源而定,這可能會對效能產生重大影響。請注意,部分 ProGuard 設定會移除追蹤記錄點,因此建議您針對要執行測試的設定移除這些規則。
合輯
在裝置上將應用程式編譯為已知狀態 (通常為速度或速度設定檔)。背景 JIT 活動可能會產生大量的效能負荷,因此您在測試執行期間重新安裝 APK 時,經常會發生這種情況。執行此項目的指令如下:
adb shell cmd package compile -m speed -f com.google.packagename
「速度」編譯模式會完整編譯應用程式;「Speed-profile」模式會根據使用應用程式期間所收集的程式碼路徑設定檔來編譯應用程式。我們很難持續且正確收集設定檔,因此如果您決定使用設定檔,請確認這些設定檔會收集您預期的資料。這些設定檔位於以下位置:
/data/misc/profiles/ref/[package-name]/primary.prof
請注意,Macrobenchmark 可讓您直接指定編譯模式。
系統注意事項
如要進行低階和高傳真測量,請校正裝置。在同一裝置和相同作業系統版本中執行 A/B 版本比較。即使使用同樣的裝置類型,效能可能會有顯著變化。
在已解鎖裝置上,請考慮使用 lockClocks
指令碼執行微基準測試。其他指令碼還包括執行下列操作:
- 將 CPU 設為固定頻率。
- 停用小型核心以設定 GPU。
- 停用熱節流。
不建議針對使用者體驗進行集中測試 (例如應用程式啟動、DoU 測試和資源浪費測試),但對於微基準測試中的降噪是不可或缺的一環。
建議您盡可能使用測試架構,例如 Macrobenchmark,以減少測量中的雜訊,避免測量不準確。
應用程式啟動速度緩慢:非必要的彈翻床活動
彈跳床活動可能會不必要的延長應用程式啟動時間,因此務必瞭解應用程式是否正常運作。如下列範例追蹤記錄所示,一個 activityStart
後面緊接著 activityStart
,且第一個活動並沒有繪製任何頁框。
這項作業可以在通知進入點和一般應用程式啟動進入點進行,而且通常可以透過重構來解決。舉例來說,如果您在執行其他活動之前,使用該活動來執行設定,請將程式碼分解至可重複使用的元件或程式庫中。
觸發頻繁 GC 的非必要配置
您可能會注意到,垃圾收集 (GC) 的頻率比 Systrace 中預期的發生頻率高。
在這種情況下,在長時間執行的作業期間,每 10 秒是一個指標,代表您的應用程式可能遭到不必要的分配,但有時仍會持續運作:
或者,您可能會注意到在使用記憶體分析器時,呼叫堆疊會佔大部分的分配比例。您不需要主動刪除所有配置,因為這樣可能會導致程式碼更難以維護。建議改從使用分配熱點開始。
Janky 框架
圖形管線的複雜度相對較高,為了判斷使用者最後是否看見捨棄的影格,可能會有些許細微的影響。在某些情況下,平台可以使用緩衝功能來「找回」影格。不過您可以忽略大部分的細微差異,直接從應用程式的角度輕鬆找出有問題的畫面。
如果您繪製的畫面幾乎都需在應用程式中完成,Choreographer.doFrame()
追蹤記錄點就會以 16.7 毫秒的步調 (假設是 60 FPS 裝置) 出現:
縮小並瀏覽追蹤記錄時,您有時會覺得畫面時間比較長,但因為畫面時間超過 16.7 毫秒的處理時間,所以不會造成太大影響。
實際看到一般步調遭中斷時,代表會有 Janky 頁框:
只要練習個幾次,你就能輕鬆觀看。
在某些情況下,您必須放大該追蹤記錄點,以進一步瞭解哪些檢視表會灌水,或 RecyclerView 正在執行了哪些動作。在其他情況下,您可能需要進一步檢查。
如要進一步瞭解識別 Janky 頁框並偵錯其原因,請參閱轉譯速度緩慢。
常見 RecyclerView 錯誤
- 正在進行非必要的無效化「
RecyclerView
」完整備份資料。這可能會導致畫面轉譯時間較長,並造成資源浪費。相反的,僅讓變更的資料失效,盡可能減少需要更新的檢視表數量。- 請參閱呈現動態資料,瞭解如何避免花費昂貴的
notifyDatasetChanged()
呼叫,這會導致內容更新而非全面取代。
- 請參閱呈現動態資料,瞭解如何避免花費昂貴的
- 無法正確支援巢狀
RecyclerView
,導致內部RecyclerView
每次都重新完整建立。- 每個巢狀內部
RecyclerView
都應該有RecycledViewPool
設定,確保檢視表可在內部RecyclerView
之間回收。
- 每個巢狀內部
- 資料量預先擷取不足,或是未及時預先擷取。快速到達捲動清單的底部,且需要等待伺服器出現更多資料會令人不快。儘管就技術上來說這不算「資源浪費」,因為並未遺失任何頁框期限,但修改預先擷取的時間和數量可以是一種明顯的 UX 改進,這樣使用者就不必等待資料。