此文件可協助您找出並修正應用程式的主要效能問題。
主要效能問題
許多問題都可能導致應用程式效能低落,以下列舉幾種應用程式中應注意的常見問題:
- 啟動延遲
啟動延遲時間是從輕觸應用程式圖示、通知或其他進入點開始,直到使用者資料顯示在畫面上所需的時間。
請在應用程式中確立下列啟動目標:
在 500 毫秒內完成冷啟動。如果系統記憶體中沒有啟動應用程式的記錄,就會出現「冷啟動」。這是應用程式自重新啟動,或使用者/系統終止應用程式的程序後,首次啟動時會出現的情形。
相反地,如果應用程式已在背景執行,就會發生「暖啟動」。冷啟動需要系統處理大部分作業,因為系統必須從儲存空間載入各種項目並初始化應用程式。因此,請設法將冷啟動的時間設為在 500 毫秒以下。
讓 P95 和 P99 延遲時間非常接近中位數延遲時間。如果應用程式需要很長的時間才能啟動,會導致使用者體驗不佳。此外,在應用程式啟動的重要路徑中,處理序間通訊 (IPC) 和不必要的 I/O 可能會遭遇鎖定爭用的情況,並造成這些不一致的問題。
- 捲動時卡頓
「卡頓」一詞是指系統無法依要求的頻率 (例如 60 Hz 或更高) 及時建構和提供影格,導致畫面出現斷斷續續的情況。卡頓問題在捲動時最容易出現,舉例來說,因為應用程式轉譯內容所需的時間比系統畫面的持續時間還長,所以畫面移動時會出現卡頓情形。卡頓問題在捲動時最容易出現,舉例來說,因為應用程式轉譯內容所需的時間比系統畫面的持續時間還長,所以畫面移動時會在一或多個畫面中暫停,導致原本應該流暢的動畫流程變得斷斷續續。
應用程式的刷新率須定為 90 Hz。傳統的轉譯率為 60 Hz,但許多新型的裝置在使用者互動 (例如捲動) 時都是在 90 Hz 的模式下運作,而且有些裝置甚至支援更高的頻率 (最高 120 Hz)。
如要查看裝置在特定時間的刷新率,請依序輕觸「偵錯」部分的「開發人員選項」>「顯示刷新率」,啟用在畫面上疊加顯示刷新率的功能。
- 轉換不順暢
這個問題會在互動期間發生,例如切換分頁或載入新活動時。此類轉換的動畫應自然流暢,不得出現延遲或視覺效果閃爍的情況。
- 電源效率不佳
執行作業會消耗電池電力,而執行不必要的作業會降低電池續航力。
記憶體配置 (在程式碼中建立新物件) 可能會導致系統作業負載增加。這是因為配置作業不僅須透過 Android 執行階段 (ART) 完成,稍後釋出這些物件 (「垃圾收集」) 也需要時間與操作。所幸,現在的配置和收集速度都比以前更快,效率也更高,對於暫存物件而言更是如此。因此,儘管以往的最佳做法是盡可能避免配置物件,現在我們會建議您以最適合應用程式和架構的方式執行操作;考量到 ART 的能力,冒著風險使用無法維護的程式碼來減少配置,並不是最理想的做法。
不過,我們建議的做法須耗費一定工夫,因此請留意若在內部迴圈中配置多個物件,可能會導致效能問題。
辨別問題
辨別效能問題並進行補救的建議工作流程如下:
- 辨別並檢查下列關鍵使用者旅程:
- 常見的啟動流程,包括透過啟動器和通知啟動的情形。
- 使用者捲動資料的畫面。
- 畫面之間的轉換。
- 長時間執行的流程,例如導航或播放音樂。
- 在上述流程期間使用以下偵錯工具進行檢查:
- Perfetto:可讓您利用精確的時間資料瞭解整部裝置中的運作情況。
- 記憶體分析器:可讓您查看堆積上的記憶體配置情形。
- Simpleperf:透過火焰圖檢視在特定時間之內占用大多數 CPU 的函式呼叫。如果發現某些項目在 Systrace 中花費的時間較長,但又不知道原因為何,Simpleperf 就可以提供其他資訊。
如要瞭解這些效能問題並進行偵錯,請務必手動對個別測試偵錯。以上步驟無法以分析匯總資料取代。不過,在自動化測試和實際環境中設定指標收集作業也很重要,因為這可協助您瞭解使用者實際可看到的畫面,並找出可能發生迴歸的時間點:
- 啟動流程
- 實際環境指標:Play 管理中心啟動時間
- 研究室測試:使用 Macrobenchmark 測試啟動作業
- 卡頓
- 實際環境指標
- Play 管理中心影格指標:您無法在 Play 管理中心將指標範圍縮小至特定使用者歷程,回報內容僅會說明應用程式的整體卡頓情形。
- 使用
FrameMetricsAggregator
自訂評估:您可以在特定工作流程中使用FrameMetricsAggregator
記錄卡頓指標。
- 研究室測試
- 使用 Macrobenchmark 捲動。
- Macrobenchmark 會使用包含單一使用者歷程的
dumpsys gfxinfo
指令收集影格時間。如要瞭解特定使用者歷程中卡頓的不同情況,這是可行的方式。RenderTime
指標會醒目顯示影格的繪製時間,在辨識迴歸或改善方面,此指標比卡頓影格數量更為重要。
- 實際環境指標
應用程式連結驗證問題
應用程式連結是一種以網站網址為依據的深層連結,且經過驗證屬於您的網站。以下是可能導致應用程式連結驗證失敗的原因。
- 意圖篩選器範圍:只在應用程式可回應的網址意圖篩選器中加入
autoVerify
。 - 未經驗證的通訊協定切換:未經驗證的伺服器端和子網域重新導向會被視為安全性風險,並導致驗證失敗。這會導致所有
autoVerify
連結失敗。舉例來說,如果未驗證 HTTPS 連結 (例如 example.com 到 www.example.com),就將 HTTP 連結重新導向至 HTTPS,可能會導致驗證失敗。請務必新增意圖篩選器,驗證應用程式連結。 - 無法驗證的連結:為了測試目的而新增無法驗證的連結,可能會導致系統無法驗證應用程式應用程式連結。
- 不穩定的伺服器:請確認您的伺服器可連線至用戶端應用程式。
設定應用程式進行效能分析
應用程式的基準測試必須正確設定,才能獲得可重現的正確結果並做出後續行動。請盡可能使用最接近實際工作環境的系統,同時抑制雜訊來源。下列各節說明準備測試設定時可以採用的 APK 與系統步驟,其中有些步驟僅適用於特定用途。
追蹤點
應用程式可以透過自訂追蹤記錄事件檢測程式碼。
系統擷取追蹤記錄時,追蹤記錄在每個區段中都會產生少許額外負荷 (約 5 μs),因此請避免在每種方法中都加入追蹤記錄。追蹤記錄大型工作區塊 (>0.1 毫秒),就足以提供重要的瓶頸深入分析。
APK 注意事項
偵錯變化版本可協助您進行疑難排解,還可以透過符號方式提供堆疊樣本,但這會對效能造成嚴重影響。在搭載 Android 10 (API 級別 29) 以上版本的裝置中,您可以使用資訊清單中的 profileable android:shell="true"
,啟用發布子版本的剖析功能。
請使用實際執行等級的程式碼縮減設定。視應用程式使用的資源而定,這可能會對效能產生重大影響。部分 ProGuard 設定會移除追蹤記錄點,因此建議您針對要執行測試的設定移除這些規則。
編譯
將裝置端應用程式編譯為已知狀態 (通常是 speed
,可簡化操作;或 speed-profile
,可更貼近實際效能,但這需要先暖機應用程式並傾印設定檔,或編譯應用程式的基準設定檔)。
speed
和 speed-profile
都能減少從 dex 解讀的執行程式碼量,進而減少可能造成重大干擾的背景即時 (JIT) 編譯量。只有 speed-profile
會降低從 dex 載入的執行階段類別影響。
下列指令會使用 speed
模式編譯應用程式:
adb shell cmd package compile -m speed -f com.example.packagename
speed
編譯模式會完整編譯應用程式的方法;speed-profile
模式會根據設定檔編譯應用程式的方法和類別,此設定檔中含有應用程式使用期間所收集的已用程式碼路徑。持續以正確方式收集設定檔可能並不容易,因此如果您決定使用這些設定檔,請確認設定檔收集的內容符合您預期。設定檔位於以下位置:
/data/misc/profiles/ref/[package-name]/primary.prof
系統注意事項
如要進行低層級和高保真的評估,請校準裝置。建議您在相同的裝置和相同的 OS 版本中執行 A/B 版本比較作業,因為即便使用同樣的裝置類型,效能也可能會有顯著變化。
在已解鎖裝置中,請考慮使用 lockClocks
指令碼進行 Microbenchmark 測試。此外,指令碼也可以執行以下操作:
- 將 CPU 設為固定頻率。
- 停用小型核心並設定 GPU。
- 停用熱節流。
使用 lockClocks
指令碼的這個方法不適合以使用者體驗為重的測試 (例如應用程式啟動、DoU 測試和卡頓測試),但對減少 Microbenchmark 測試中的雜訊而言,卻是不可或缺的一環。
建議盡可能使用測試架構,例如 Macrobenchmark,以減少評估中的雜訊,避免評估不準確。
應用程式啟動速度緩慢:非必要的 Trampoline 活動
Trampoline 活動可能會無端延長應用程式啟動時間,因此請務必瞭解應用程式是否涉及這類活動。在以下追蹤記錄範例中,activityStart
後面緊接著令另一個 activityStart
,且第一個活動沒有繪製任何影格。
這種情況在通知進入點和一般應用程式的啟動進入點都可能發生,而且通常可以透過重構來解決。舉例來說,如果您使用該活動在另一個活動執行前執行設定,請將這個程式碼分解到可重複使用的元件或程式庫中。
非必要配置觸發常用 GC
您可能會發現垃圾收集 (GC) 的發生頻率比 Systrace 中預期的高。
以下範例顯示,在長時間執行的作業期間,每 10 秒是一個指標,代表應用程式可能遭到不必要的分配,但有時仍會持續運作:
您可能也會發現,在使用記憶體分析器時,特定呼叫堆疊會占據大部分的分配比例。您不需要主動刪除所有配置,因為這樣可能會導致程式碼更難以維護。不妨改從使用分配熱點開始。
Janky 頁框
圖形管線的複雜度相對較高,為了判斷使用者最後是否看見捨棄的影格,可能會有些許細微的影響。在某些情況下,平台可以使用緩衝功能來「找回」影格。不過您可以忽略大部分的細微差異,直接從應用程式的角度找出有問題的影格。
如果繪製的影格幾乎都不是在應用程式中完成,Choreographer.doFrame()
追蹤點的間隔在 60 FPS 裝置上就會是 16.7 毫秒:
縮小並瀏覽追蹤記錄時,有時會發現影格完成處理的時間比較長,但因為沒有超過配額的 16.7 毫秒,所以還是可以接受:
如果您在規律的間隔中看到一處突然中斷,那就是卡頓的影格,如圖 5 所示:
您可以練習如何辨別卡頓影格。
在某些情況下,您需要放大檢視追蹤點,才能進一步瞭解哪些檢視畫面正在加載,或 RecyclerView
正在執行哪些操作。在其他情況下,則可能需要進一步檢查。
如要進一步瞭解如何識別卡頓影格、偵錯並找出原因,請參閱「轉譯速度緩慢」。
RecyclerView 常見錯誤
無故使整個 RecyclerView
的備份資料失效。這可能會導致影格轉譯時間過長,造成卡頓。您應該改變做法,確保只有已變更的資料失效,盡可能減少需更新的檢視畫面數目。
請參閱「呈現動態資料」,瞭解如何避免成本高昂的 notifyDatasetChanged()
呼叫,這類呼叫會導致內容更新而非全面取代。
如果您無法正確支援每個巢狀的 RecyclerView
,可能會導致內部 RecyclerView
每次都要全部重建。每個巢狀的內部 RecyclerView
,都必須設有 RecycledViewPool
,以確保檢視畫面可在各個內部 RecyclerView
之間回收。
如果預先擷取的資料量不足,或是未及時預先擷取,使用者就可能需要等候伺服器傳來更多資料,才能流暢捲動到清單底部。雖然技術上來說這不算卡頓,因為操作仍然符合影格顯示期限,但仍建議您修改預先擷取的時間和數量,讓使用者不必等候資料顯示。這麼一來,使用者體驗也就能大幅提升。
為應用程式偵錯
以下是各種應用程式效能偵錯方法。請觀看以下影片,概略瞭解系統追蹤功能和如何使用 Android Studio 分析器。
使用 Systrace 對應用程式啟動進行偵錯
請參閱「應用程式啟動時間」一文,瞭解應用程式啟動程序的概略說明,並觀看以下影片,瞭解系統追蹤的概略說明。
您可以在下列階段區分啟動類型:
- 冷啟動:一開始會建立新程序,但沒有暫存狀態。
- 暖啟動:在重複使用程序的同時重新建立活動,或是使用已儲存的狀態重新建立程序。
- 熱啟動:重新啟動活動,並從展開開始。
建議您使用裝置上的系統追蹤應用程式擷取系統追蹤記錄。如果是 Android 10 以上版本,請使用 Perfetto。如果是 Android 9 以下版本,請使用 Systrace。我們也建議您使用網頁式 Perfetto 追蹤記錄檢視器查看追蹤記錄檔案。詳情請參閱「系統追蹤總覽」。
請留意以下幾點:
- 監控爭用:受監控保護的資源如果發生內部競爭,可能會造成應用程式的啟動作業大幅延遲。
同步綁定資料傳輸:從應用程式的主要路徑中找出不必要的資料傳輸。如果必要的交易費用高昂,建議您與相關平台團隊合作,改善這項問題。
並行 GC:這項作業相當常見,對系統影響程度也相對較低。但如果經常發生,建議利用 Android Studio 記憶體分析器進行調查。
I/O:檢查啟動期間執行的 I/O,並尋找較嚴重的停頓問題。
其他執行緒的重大活動:這些活動可能會干擾 UI 執行緒,因此請留意啟動期間的背景作業。
建議您在應用程式啟動完成後,從應用程式角度呼叫 reportFullyDrawn
,以便改善應用程式啟動指標回報。如要進一步瞭解如何使用 reportFullyDrawn
,請參閱「顯示完整畫面所需時間」一節。您可以透過 Perfetto 追蹤記錄處理器擷取 RFD 定義的開始時間,並發出使用者可見的追蹤記錄事件。
在裝置上使用系統追蹤
您可以使用名為「系統追蹤」的系統層級應用程式,在裝置上擷取系統追蹤記錄。這個應用程式可讓您直接從裝置錄製追蹤記錄,而不用把裝置連結到 adb
。
使用 Android Studio 記憶體分析器
您可以使用 Android Studio 記憶體分析器檢查記憶體流失或不當使用模式可能造成的記憶體壓力。可讓您即時查看物件配置情況。
您可以使用記憶體分析器追蹤垃圾資訊收集的發生原因和頻率,並根據這項資訊修正應用程式的記憶體問題。
如要剖析應用程式記憶體,請執行下列步驟:
偵測記憶體問題。
針對您要著重的使用者歷程,記錄記憶體分析工作階段。請查看物件計數是否持續增加 (如圖 7 所示),這最終會導致 GC 作業 (如圖 8 所示)。
找出會增加記憶體壓力的使用者歷程後,請分析記憶體壓力的根本原因。
診斷記憶體空間壓力熱點。
選取時間軸範圍,以便視覺化呈現「Allocations」和「Shallow Size」,如圖 9 所示。
這些資料的分類方式有很多種。以下提供幾個例子,說明如何透過各檢視畫面分析問題。
依類別排序:如果您想找出會產生物件的類別,而這些物件會從記憶體集區快取或重複使用,這會很實用。
舉例來說,如果您看到應用程式每秒建立 2,000 個「Vertex」類別的物件,則「配置」計數器每秒會增加 2,000 個,您會在依類別排序時看到這種狀況。如果您想重複使用這些物件以避免產生垃圾,請實作記憶體集區。
依呼叫堆疊編排:當記憶體配置到熱路徑,例如位於迴圈內或有特定功能需配置多項工作,依呼叫堆疊編排會是實用做法。
淺層大小:只會追蹤物件本身占用的記憶體。這類型最適合追蹤主要由原始值組成的簡易類別。
Retained Size:顯示物件和物件單獨參照的參照所需的記憶體總量。在追蹤複雜物件造成的記憶體壓力時,參考保留大小較為適合。如要取得這個值,請執行完整記憶體傾印作業,如圖 10 所示,並將「Retained Size」新增為資料欄,如圖 11 所示。
評估最佳化措施的影響。
GC 更明顯,也更容易評估記憶體最佳化帶來的影響。當最佳化作業成功釋放記憶體壓力,您會看到次數較少的 GC。
如要評估最佳化效益,請在分析器時間軸上測量垃圾資訊收集之間的間隔時間長度。您可以看到垃圾收集之間的間隔時間較長。
記憶體改善項目的最終影響如下:
- 如果應用程式並非持續受到記憶體壓力,就較不會因記憶體空間不足而遭到系統終止。
- 減少垃圾資訊收集的次數可以改善資源浪費的相關指標,尤其是 P99。這是因為垃圾收集事件會造成 CPU 爭用情形,導致轉譯工作在垃圾收集期間遭到延遲。
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- 應用程式啟動分析與最佳化 {:#app-startup-analysis-optimization}
- 凍結影格
- 撰寫 Macrobenchmark