시스템 앱의 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입니다. 하지만 최악의 경우 약 5밀리초가 걸릴 수 있습니다. Perfetto 트레이스의 vc#onDraw 슬라이스를 참고하세요.

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

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

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

메모리

ViewCapture 트레이스를 위한 Perfetto의 메서드는 과도한 메모리 사용을 방지하기 위해 사전 정의된 메모리 공간이 있는 단일 링 버퍼를 사용합니다. 이 접근 방식은 각 창에 별도의 링 버퍼를 사용하지 않음으로써 과도한 메모리 소비를 방지하지만 각 프레임에 대해 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();
       }
        ...
      }