系統應用程式中的 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 函式擷取每個屬性值,會導致每個 View 需要額外呼叫許多函式,耗時 110 微秒。

因此,在 AOT 中啟用 ViewCapture 會對系統效能造成負面影響,並導致卡頓。由於這些效能和記憶體限制,這項做法尚未準備好採用 AOT。我們建議您只在實驗室和本機偵錯時使用 ViewCapture。

記憶體

Perfetto 的 ViewCapture 追蹤記錄方法會使用單一環形緩衝區,該緩衝區具有預先定義的記憶體占用空間,可避免過度使用記憶體。這種做法可避免為每個視窗使用個別的環緩衝區,進而避免過度耗用記憶體,但無法解決在每個影格中,為每個狀態在 Perfetto 中儲存整個檢視區塊階層的問題。錄製單一視窗 (例如 NexusLauncher) 時,可在 10 MB 緩衝區中產生超過 30 秒的 ViewCapture 資料。不過,從系統使用者介面擷取超過 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();
       }
        ...
      }