跳到主要內容

效能分析

效能分析是分析應用程式的效能、資源使用率和行為的過程,以找出潛在的瓶頸或效率低下的地方。值得使用效能分析工具,以確保你的應用程式在不同的裝置和條件下都能順暢運作。

對於 iOS,Instruments 是一個非常寶貴的工具,而在 Android 上,你應該學習使用 Android Studio Profiler

但首先,請務必關閉開發模式! 你應該在應用程式日誌中看到 __DEV__ === false, development-level warning are OFF, performance optimizations are ON

使用系統追蹤分析 Android UI 效能

Android 支援超過 1 萬種不同的手機,並且通用到支援軟體渲染:框架架構和跨多個硬體目標進行通用的需求,不幸的是,相較於 iOS,你獲得的免費資源較少。但有時,有些事情你可以改進 —— 而且很多時候,這根本不是原生程式碼的錯!

偵錯這種卡頓的第一步是回答一個基本問題:在每個 16 毫秒的影格中,你的時間都花在哪裡。為此,我們將使用 Android Studio 中內建的系統追蹤效能分析器

1. 收集追蹤資料

首先,透過 USB 將出現卡頓問題的裝置連接到你的電腦。在 Android Studio 中開啟你的專案的 android 資料夾,在右上角窗格中選擇你的裝置,然後以可效能分析模式執行你的專案

當你的應用程式以可效能分析模式建置並在裝置上執行時,讓你的應用程式到達你想要分析的導航/動畫之前的狀態,然後在 Android Studio Profiler 窗格中啟動「捕捉系統活動」任務

一旦追蹤開始收集,執行你關心的動畫或互動。然後按下「停止錄製」。你現在可以直接在 Android Studio 中檢查追蹤資料。或者,你可以在「過去的錄製」窗格中選擇它,按下「匯出錄製」,然後在像 Perfetto 這樣的工具中開啟它。

2. 讀取追蹤資料

在 Android Studio 或 Perfetto 中開啟追蹤資料後,你應該會看到類似這樣的東西

Example

提示

使用 WASD 鍵進行平移和縮放。

確切的 UI 可能會有所不同,但以下說明將適用於你使用的任何工具。

啟用 VSync 突出顯示

勾選螢幕右上角的這個核取方塊,以突出顯示 16 毫秒的影格邊界

Enable VSync Highlighting

你應該會看到如螢幕截圖中的斑馬條紋。如果你沒有看到,請嘗試在不同的裝置上進行效能分析:眾所周知,三星在顯示垂直同步方面存在問題,而 Nexus 系列通常非常可靠。

3. 尋找你的程序

捲動直到你看到你的套件名稱(的一部分)。在本例中,我正在分析 com.facebook.adsmanager 的效能,由於核心中愚蠢的執行緒名稱限制,它顯示為 book.adsmanager

在左側,你會看到一組執行緒,它們對應於右側的時間軸列。為了我們的目的,我們關心幾個執行緒:UI 執行緒(具有你的套件名稱或名稱 UI 執行緒)、mqt_jsmqt_native_modules。如果你在 Android 5+ 上執行,我們也關心 Render Thread。

  • UI 執行緒。 這是標準 Android 測量/佈局/繪製發生的地方。右側的執行緒名稱將是你的套件名稱(在我的例子中是 book.adsmanager)或 UI 執行緒。你在這個執行緒上看到的事件應該看起來像這樣,並且與 ChoreographertraversalsDispatchUI 有關

    UI Thread Example

  • JS 執行緒。 這是 JavaScript 執行的地方。執行緒名稱將是 mqt_js<...>,具體取決於你的裝置上的核心的合作程度。如果它沒有名稱,要識別它,請尋找類似 JSCallBridge.executeJSCall 等的東西

    JS Thread Example

  • 原生模組執行緒。 這是原生模組呼叫(例如 UIManager)執行的地方。執行緒名稱將是 mqt_native_modules<...>。為了在後一種情況下識別它,請尋找類似 NativeCallcallJavaModuleMethodonBatchComplete 的東西

    Native Modules Thread Example

  • 額外:Render Thread。 如果你使用的是 Android L (5.0) 及更高版本,你的應用程式中也將有一個渲染執行緒。這個執行緒生成用於繪製 UI 的實際 OpenGL 命令。執行緒名稱將是 RenderThread<...>。為了在後一種情況下識別它,請尋找類似 DrawFramequeueBuffer 的東西

    Render Thread Example

找出問題根源

流暢的動畫應該看起來像這樣

Smooth Animation

顏色的每次變化都是一個影格 —— 請記住,為了顯示一個影格,我們所有的 UI 工作都需要在 16 毫秒的時間段結束前完成。請注意,沒有執行緒的工作接近影格邊界。像這樣渲染的應用程式以 60 FPS 渲染。

但是,如果你注意到卡頓,你可能會看到類似這樣的東西

Choppy Animation from JS

請注意,JS 執行緒幾乎一直在執行,並且跨越影格邊界!這個應用程式不是以 60 FPS 渲染。在這種情況下,問題出在 JS 中

你也可能會看到類似這樣的東西

Choppy Animation from UI

在這種情況下,UI 和渲染執行緒是工作跨越影格邊界的執行緒。我們試圖在每個影格上渲染的 UI 需要完成太多工作。在這種情況下,問題出在正在渲染的原生視圖中

此時,你將獲得一些非常有用的資訊,以告知你的後續步驟。

解決 JavaScript 問題

如果你發現了 JS 問題,請在你正在執行的特定 JS 中尋找線索。在上面的場景中,我們看到 RCTEventEmitter 在每個影格中被多次呼叫。以下是上面追蹤資料中 JS 執行緒的放大圖

Too much JS

這似乎不太對勁。為什麼它被呼叫如此頻繁?它們實際上是不同的事件嗎?這些問題的答案可能取決於你的產品程式碼。而且很多時候,你會想研究 shouldComponentUpdate

解決原生 UI 問題

如果你發現了原生 UI 問題,通常有兩種情況

  1. 你試圖在每個影格中繪製的 UI 在 GPU 上涉及太多工作,或者
  2. 你在動畫/互動期間建構新的 UI(例如,在捲動期間載入新內容)。

GPU 工作量過大

在第一種情況下,你會看到一個追蹤資料,其中 UI 執行緒和/或 Render Thread 看起來像這樣

Overloaded GPU

請注意,在跨越影格邊界的 DrawFrame 中花費了很長時間。這是等待 GPU 從前一個影格排空其命令緩衝區所花費的時間。

為了緩解這種情況,你應該

  • 研究使用 renderToHardwareTextureAndroid 用於正在動畫/轉換的複雜靜態內容(例如 Navigator 滑動/Alpha 動畫)
  • 確保你沒有使用 needsOffscreenAlphaCompositing,預設情況下它是禁用的,因為在大多數情況下,它會大大增加 GPU 上每個影格的負載。

在 UI 執行緒上建立新視圖

在第二種情況下,你會看到更像這樣的東西

Creating Views

請注意,首先 JS 執行緒思考了一會兒,然後你看到原生模組執行緒上完成了一些工作,然後是 UI 執行緒上昂貴的遍歷。

除非你能將建立新 UI 的時間推遲到互動之後,或者你能簡化你正在建立的 UI,否則沒有快速緩解這種情況的方法。react native 團隊正在為此開發基礎架構層級的解決方案,這將允許在主執行緒之外建立和配置新的 UI,從而使互動能夠順利繼續。

尋找原生 CPU 熱點

如果問題似乎出現在原生端,你可以使用 CPU 熱點分析器來獲取有關正在發生的事情的更多詳細資訊。開啟 Android Studio Profiler 面板並選擇「尋找 CPU 熱點(Java/Kotlin 方法錄製)」。

選擇 Java/Kotlin 錄製

請務必選擇「尋找 CPU 熱點 (Java/Kotlin 錄製)」,而不是「尋找 CPU 熱點(呼叫堆疊範例)」。它們有相似的圖示,但做的事情不同。

執行互動並按下「停止錄製」。錄製是資源密集型的,因此請保持互動簡短。然後,你可以直接在 Android Studio 中檢查結果追蹤資料,或匯出它並在像 Firefox Profiler 這樣的線上工具中開啟它。

與系統追蹤不同,CPU 熱點分析速度較慢,因此它不會給你準確的測量結果。但是,它應該讓你了解正在呼叫哪些原生方法,以及在每個影格期間時間的比例分佈。