系統應用程式中的 ViewCapture

ViewCapture 是一種軟體工具,可擷取附加至所連結視窗的檢視區塊屬性 (例如位置、大小、比例和可見性)。ViewCapture 會擷取視窗中各種檢視區塊的相關資訊和屬性,讓您瞭解特定時間點的使用者體驗狀態,並追蹤一段時間內的變化。

螢幕錄影功能可以顯示特定時間的檢視區塊狀態,以及狀態的變化情形,但會耗用大量 CPU 資源,可能影響效能。ViewCapture 工具對資源的影響較小,因此可以更頻繁地啟用。此外,ViewCapture 會在檢視區塊層級逐格顯示視覺化內容框架,因此相較於螢幕錄影,檢查特定時刻的檢視區塊狀態更直接。

本頁說明如何在系統應用程式中導入 ViewCapture。

使用

ViewCapture.java 會實作 onDrawListener 的執行個體,並在繪圖程序期間收集 ViewCapture 追蹤記錄。每次重繪影格時,系統都會從視窗的根層級檢視畫面開始,遍歷檢視區塊樹狀結構階層。ViewCapture 會使用公開 View.java getter 方法,擷取值並複製到背景執行緒,以提升效能。ViewCapture 實作項目會使用 captureViewTree 檢查檢視區塊是否髒污或失效,藉此避免遍歷整個檢視區塊階層,進而最佳化這個程序。captureViewTree 僅適用於系統應用程式,且屬於 UnsupportedAppUsage API。只有目標 SDK 版本符合條件的應用程式,才能使用這項 API。

限制

本節說明 ViewCapture 的效能和記憶體限制。

效能

ViewCapture 效能的平均主執行緒負荷為 195 微秒。不過,在最糟的情況下,可能需要約 5 毫秒。請參閱 Perfetto 追蹤記錄中的 vc#onDraw 切片。

主要造成額外費用的動作如下:

  1. 即使經過修剪,遍歷階層仍需 50 微秒。
  2. 從自由表分配器提取物件來儲存檢視區塊屬性副本的費用為 20 微秒。
  3. 透過 getter 函式擷取每個屬性值,會導致每個檢視區塊產生許多額外的函式呼叫,耗費 110 微秒。

因此,在永遠追蹤 (AOT) 模式中啟用 ViewCapture 會對系統效能造成負面影響,並導致抖動。由於這些效能和記憶體限制,這種方法還不適用於 AOT。建議只在實驗室和本機偵錯時使用 ViewCapture。

記憶體

Perfetto 的 ViewCapture 追蹤方法會使用單一環形緩衝區和預先定義的記憶體用量,避免記憶體用量過高。這種做法可避免為每個視窗建立個別的環形緩衝區,進而防止記憶體耗用過多。不過,這無法解決在每個影格的 Perfetto 中,儲存每個狀態的完整檢視區塊階層的問題。錄製單一視窗 (例如 NexusLauncher) 時,10 MB 的緩衝區可能會產生超過 30 秒的 ViewCapture 資料。如要從系統 UI 擷取超過 30 個視窗,需要較大的緩衝區或大幅縮短錄製時間。

操作說明

如要在系統應用程式中啟用 ViewCapture,請按照下列指示操作:

  1. 將依附元件新增至 Android.bp 檔案,如「啟動器程式碼」所示。

    android_library {
        name: "YourLib",
        static_libs: [
              ...
            "//frameworks/libs/systemui:view_capture",
              ...
        ],
        platform_apis: true,
        privileged: true,
    }
    
  2. 建立視窗時,請建立 ViewCapture 執行個體,例如:

    • 範例 1

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        ...
        mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
      }
      
    • 範例 2

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (enableViewCaptureTracing()) {
            mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
              .startCapture(getRootView(), ".NotificationShadeWindowView");
        }
        ...
      }
      
  3. 如以下範例所示,在終結視窗時,請關閉 ViewCapture 執行個體:

    • 範例 1

      @Override
      public void onDestroy() {
        ...
        if (mViewCapture != null) mViewCapture.close();
      }
      
    • 範例 2

      @Override
      protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mViewCaptureCloseable != null) {
            mViewCaptureCloseable.close();
       }
        ...
      }