시스템 앱의 ViewCapture

ViewCapture는 연결된 창에 연결된 뷰의 속성 (예: 위치, 크기, 비율, 공개 상태)을 캡처하는 소프트웨어 도구입니다. ViewCapture는 창 내의 다양한 뷰와 그 속성에 관한 정보를 캡처하여 특정 시점의 사용자 환경 상태를 파악하고 시간 경과에 따른 변경사항을 추적할 수 있도록 지원합니다.

화면 녹화는 특정 시점의 뷰 상태를 시각화하고 뷰가 어떻게 변경되는지 보여줄 수 있지만 상당한 CPU 리소스가 필요하며 성능에 영향을 줄 수 있습니다. ViewCapture 도구는 리소스에 미치는 영향이 적으며 더 자주 사용 설정할 수 있습니다. 또한 ViewCapture는 뷰 수준에서 프레임별로 시각화를 표시하므로 화면 녹화에 비해 특정 순간의 뷰 상태를 더 간단하게 검사할 수 있습니다.

이 페이지에서는 시스템 앱에서 ViewCapture를 온보딩하는 방법을 설명합니다.

사용

ViewCapture.javaonDrawListener 인스턴스를 구현하고 그리기 프로세스 중에 ViewCapture 트레이스를 수집합니다. 각 프레임 다시 그리기에서는 창의 루트 뷰에서 시작하는 뷰 트리 계층 구조의 순회를 트리거합니다. ViewCapture는 공개 View.java getter 메서드를 사용하여 값을 가져오고 백그라운드 스레드에 복사하여 성능을 개선합니다. ViewCapture 구현은 captureViewTree를 사용하여 뷰가 더티인지 또는 무효화되었는지 확인하여 이 프로세스를 최적화하므로 전체 뷰 계층 구조의 순회를 방지합니다. captureViewTree는 시스템 앱에만 제공되며 UnsupportedAppUsage API의 일부입니다. 이 API의 사용은 타겟 SDK 버전에 따라 앱으로 제한됩니다.

제한사항

이 섹션에서는 ViewCapture의 성능 및 메모리 제한사항을 설명합니다.

성능

ViewCapture 성능의 평균 기본 스레드 오버헤드는 195μs입니다. 하지만 최악의 경우 약 5ms가 걸릴 수 있습니다. Perfetto 트레이스에서 vc#onDraw 슬라이스를 참고하세요.

오버헤드 비용은 주로 다음 작업으로 인해 발생합니다.

  1. 잘라낸 경우에도 계층 구조를 탐색하는 데 50μs가 소요됩니다.
  2. 뷰 속성의 사본을 저장하기 위해 사용 가능 목록 할당자에서 객체를 가져오는 데는 20μs가 소요됩니다.
  3. getter 함수를 통해 각 속성 값을 가져오면 뷰당 추가 함수 호출이 많이 발생하여 110μs가 소요됩니다.

따라서 항상 사용 설정 추적 (AOT)에서 ViewCapture를 사용 설정하면 시스템 성능에 부정적인 영향을 미치고 버벅거림이 발생합니다. 이러한 성능 및 메모리 제한으로 인해 이 접근 방식은 AOT에 적합하지 않습니다. ViewCapture는 실험실 및 로컬 디버깅에만 사용하는 것이 좋습니다.

메모리

Perfetto의 ViewCapture 트레이스 메서드는 과도한 메모리 사용을 방지하기 위해 사전 정의된 메모리 공간이 있는 단일 링 버퍼를 사용합니다. 이 접근 방식은 각 창에 별도의 링 버퍼를 사용하지 않아 과도한 메모리 소비를 방지합니다. 하지만 각 프레임의 Perfetto에서 모든 상태에 대해 전체 뷰 계층 구조를 저장하는 문제는 해결되지 않습니다. NexusLauncher와 같은 단일 창을 녹화하면 10MB 버퍼에 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();
       }
        ...
      }