ViewCapture em apps do sistema

O ViewCapture é uma ferramenta de software que captura as propriedades das visualizações (como local, tamanho, escala e visibilidade) anexadas às janelas em que está conectado. O ViewCapture captura informações sobre as várias visualizações em uma janela e as propriedades delas, informando o estado da experiência do usuário em momentos específicos e rastreando mudanças ao longo do tempo.

As gravações de tela podem mostrar o estado de uma visualização em um momento específico e como ela muda, mas exigem muitos recursos da CPU e podem afetar o desempenho. A ferramenta ViewCapture tem menos impacto nos recursos e pode ser ativada com mais frequência. Além disso, o ViewCapture mostra visualizações frame a frame no nível da visualização, facilitando a inspeção do estado da visualização em momentos específicos em comparação com as gravações de tela.

Esta página descreve como integrar o ViewCapture em apps do sistema.

Uso

O ViewCapture.java implementa uma instância de onDrawListener e coleta um rastreamento de ViewCapture durante o processo de desenho. Cada atualização de frame aciona uma percorrida da hierarquia da árvore de visualização, começando pela visualização raiz da janela. O ViewCapture usa métodos getter públicos View.java para buscar e copiar valores para uma linha de execução em segundo plano e melhorar a performance. A implementação do ViewCapture otimiza esse processo verificando se uma visualização está suja ou invalidada usando captureViewTree, evitando assim a travessia de toda a hierarquia de visualização. captureViewTree está disponível apenas para apps do sistema e faz parte da API UnsupportedAppUsage. O uso dessa API é limitado a apps com base na versão do SDK de destino.

Limitações

Esta seção descreve as limitações de desempenho e memória do ViewCapture.

Desempenho

O overhead médio da linha de execução principal para o desempenho do ViewCapture é de 195 μs. No entanto, no pior cenário, pode levar aproximadamente 5 ms. Consulte a fração vc#onDraw no rastreamento do Perfetto.

Os custos indiretos são principalmente devido às seguintes ações:

  1. Percorrer a hierarquia custa 50 μs, mesmo quando ela é reduzida.
  2. Extrair objetos de um alocador de lista livre para armazenar cópias de propriedades de visualização custa 20 μs.
  3. Buscar cada valor de propriedade usando uma função getter resulta em muitas chamadas de função adicionais por visualização, custando 110 μs.

Por isso, ativar o ViewCapture no rastreamento sempre ativo (AOT, na sigla em inglês) afeta negativamente o desempenho do sistema e causa instabilidade. Devido a essas limitações de desempenho e memória, essa abordagem não está pronta para AOT. Recomendamos o ViewCapture apenas para depuração local e de laboratório.

Memória

O método do Perfetto para rastreamentos do ViewCapture usa um único buffer de anel com um consumo de memória predefinido para evitar o uso excessivo de memória. Essa abordagem evita o consumo excessivo de memória, já que não usa buffers de anel separados para cada janela. No entanto, isso não resolve o problema de armazenar toda a hierarquia de visualização para cada estado no Perfetto em cada frame. A gravação de uma única janela, como o NexusLauncher, pode produzir mais de 30 segundos de dados do ViewCapture em um buffer de 10 MB. Para capturar mais de 30 janelas da interface do sistema, é necessário um buffer maior ou um tempo de gravação consideravelmente menor.

Instruções

Para integrar o ViewCapture em apps do sistema, siga estas instruções:

  1. Adicione a dependência ao arquivo Android.bp, conforme mostrado no código do iniciador.

    android_library {
        name: "YourLib",
        static_libs: [
              ...
            "//frameworks/libs/systemui:view_capture",
              ...
        ],
        platform_apis: true,
        privileged: true,
    }
    
  2. Crie uma instância de ViewCapture ao criar a janela, por exemplo:

    • Exemplo 1:

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

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (enableViewCaptureTracing()) {
            mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
              .startCapture(getRootView(), ".NotificationShadeWindowView");
        }
        ...
      }
      
  3. Feche a instância ViewCapture ao destruir a janela, conforme mostrado nos exemplos a seguir:

    • Exemplo 1:

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

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