Medir o tempo de carregamento e a renderização da tela com o Monitoramento de desempenho do Firebase

1. Introdução

Última atualização:11/03/2021

Por que precisamos medir a performance das visualizações?

As visualizações são uma parte essencial dos apps Android e afetam diretamente a experiência do usuário. Por exemplo, sua atividade ou fragmento contém a interface que inclui os componentes de visualização com que os usuários interagem. Os usuários não conseguem ver todo o conteúdo da interface até que ela seja completamente desenhada na tela. Telas lentas e travadas prejudicam diretamente a interação do usuário com o app e criam uma experiência ruim.

O Monitoramento de desempenho do Firebase não fornece essas métricas de desempenho de maneira imediata?

O Monitoramento de desempenho do Firebase captura automaticamente alguns dados de desempenho assim que é instalado, como o tempo de inicialização do app (ou seja, o tempo de carregamento da primeira atividade) e o desempenho de renderização da tela (ou seja, frames lentos e congelados para atividades, mas não para fragmentos). No entanto, os apps do setor geralmente não têm muitas atividades, mas sim uma atividade e vários fragmentos. Além disso, muitos apps geralmente implementam visualizações personalizadas próprias para casos de uso mais complexos. Por isso, é útil entender como medir o tempo de carregamento e o desempenho de renderização de tela de atividades e fragmentos instrumentando traces de código personalizados no app. É fácil estender este codelab para medir o desempenho de componentes de visualização personalizada.

O que você aprenderá

  • Como adicionar o Monitoramento de desempenho do Firebase a um app Android
  • Entender o carregamento de uma atividade ou um fragmento
  • Como instrumentar traces de código personalizados para medir o tempo de carregamento de uma atividade ou um fragmento
  • Entender a renderização de tela e o que é um frame lento/congelado
  • Como instrumentar traces de código personalizados com métricas para registrar telas lentas/congeladas
  • Como ver as métricas coletadas no console do Firebase

O que é necessário

  • Android Studio 4.0 ou versões mais recentes
  • Um dispositivo/emulador Android
  • Java versão 8 ou mais recente

2. Etapas da configuração

Buscar o código

Execute os comandos a seguir para clonar o código de amostra deste codelab. Isso vai criar uma pasta chamada codelab-measure-android-view-performance na sua máquina:

$ git clone https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$ cd codelab-measure-android-view-performance

Se você não tiver o git na sua máquina, também poderá fazer o download do código diretamente do GitHub.

Importe o projeto measure-view-performance-start para o Android Studio. Você provavelmente vai encontrar alguns erros de compilação ou um aviso sobre um arquivo google-services.json ausente. Vamos corrigir isso na próxima seção desta etapa.

Neste codelab, vamos usar o plug-in Firebase Assistente para registrar nosso app Android em um projeto do Firebase e adicionar os arquivos de configuração, plug-ins e dependências necessários ao nosso projeto Android. Tudo isso no Android Studio.

Conectar seu app ao Firebase

  1. Acesse Android Studio/Ajuda > Verificar atualizações para garantir que você esteja usando as versões mais recentes do Android Studio e do Firebase Assistente.
  2. Selecione Ferramentas > Firebase para abrir o painel Assistente.
    e791bed0999db1e0.png
  3. Escolha Monitoramento de desempenho para adicionar ao app e clique em Começar a usar o Monitoramento de desempenho.
  4. Clique no botão para criar um projeto e insira um nome (por exemplo, Measure Performance Codelab).
  5. Clique em Continuar.
  6. Se solicitado, leia e aceite os Termos do Firebase e clique em Continuar.
  7. (Opcional) Ative a assistência de IA no console do Firebase (chamada de "Gemini no Firebase").
  8. Neste codelab, você não precisa do Google Analytics. Portanto, desative a opção do Google Analytics.
  9. Em seguida, uma caixa de diálogo vai aparecer para Conectar seu novo app do Firebase ao projeto do Android Studio.
    42c498d28ead2b77.png
  10. De volta ao Android Studio, no painel Assistente, você vai ver a confirmação de que seu app está conectado ao Firebase.
    dda8bdd9488167a0.png

Adicionar o Monitoramento de desempenho ao app

No painel Assistente do Android Studio, clique em Adicionar o Monitoramento de desempenho ao seu app.

Uma caixa de diálogo vai aparecer para Aceitar mudanças. Depois disso, o Android Studio vai sincronizar seu app para garantir que todas as dependências necessárias foram adicionadas.

9b58145acc4be030.png

Por fim, você vai ver a mensagem de sucesso no painel Assistente do Android Studio, indicando que todas as dependências foram configuradas corretamente.

aa0d46fc944e0c0b.png

Como uma etapa adicional, ative a geração de registros de depuração seguindo as instruções na etapa "(Opcional) Ativar a geração de registros de depuração". As mesmas instruções também estão disponíveis na documentação pública.

3. Executar o app

Se você tiver integrado o app ao SDK do Monitoramento de desempenho, o projeto será compilado. No Android Studio, clique em Executar > Executar "app" para criar e executar o app no dispositivo/emulador Android conectado.

O app tem dois botões que levam a uma atividade e um fragmento correspondentes, assim:

410d8686b4f45c33.png

Nas próximas etapas deste codelab, você vai aprender a medir o tempo de carregamento e o desempenho de renderização da tela da sua atividade ou fragmento.

4. Entender o carregamento de uma atividade ou um fragmento

Nesta etapa, vamos aprender o que o sistema faz durante o carregamento de uma atividade ou um fragmento.

Entender o carregamento de uma atividade

Para uma atividade, o tempo de carregamento é definido como o tempo desde a criação do objeto de atividade até que o primeiro frame seja completamente desenhado na tela. É quando o usuário vê a interface completa da atividade pela primeira vez. Para medir se o app foi totalmente renderizado, use o método reportFullyDrawn() para medir o tempo decorrido entre a inicialização do app e a exibição completa de todos os recursos e hierarquias de visualização.

Em um nível alto, quando o app chama startActivity(Intent), o sistema executa automaticamente os seguintes processos. Cada processo leva tempo para ser concluído, o que aumenta a duração entre a criação da atividade e o momento em que o usuário vê a interface dela na tela.

c20d14b151549937.png

Como entender o carregamento de um fragmento

Assim como na atividade, o tempo de carregamento de um fragmento é definido como o tempo desde que o fragmento é anexado à atividade host até que o primeiro frame da visualização do fragmento seja completamente desenhado na tela.

5. Medir o tempo de carregamento de uma atividade

Atrasos no primeiro frame podem levar a uma experiência ruim do usuário. Por isso, é importante entender quanto tempo de atraso no carregamento inicial seus usuários estão enfrentando. É possível instrumentar um trace de código personalizado para medir esse tempo de carregamento:

  1. Inicie o rastreamento de código personalizado (chamado TestActivity-LoadTime) na classe Activity assim que o objeto Activity for criado.

TestActivity.java

public class TestActivity extends AppCompatActivity {   
    // TODO (1): Start trace recording as soon as the Activity object is created.
    private final Trace viewLoadTrace = FirebasePerformance.startTrace("TestActivity-LoadTime");

    // ...

}
  1. Substitua o callback onCreate() e receba a visualização adicionada pelo método setContentView().
@Override     
public void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);          

    // Current Activity's main View (as defined in the layout xml file) is inflated after this            
    setContentView(R.layout.activity_test);          

    // ...

    // TODO (2): Get the View added by Activity's setContentView() method.         
    View mainView = findViewById(android.R.id.content);     

    // ...
}
  1. Incluímos uma implementação de FistDrawListener, que tem dois callbacks: onDrawingStart() e onDrawingFinish() . Consulte a próxima seção abaixo para mais detalhes sobre FirstDrawListener e o que pode afetar a performance dele. Registre o FirstDrawListener no final do callback onCreate() da atividade. Pare o viewLoadTrace no callback onDrawingFinish().

TestActivity.java

    // TODO (3): Register the callback to listen for first frame rendering (see
    //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is
    //  finished.
    FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {              
        @Override             
        public void onDrawingStart() {       
          // In practice you can also record this event separately
        }

        @Override             
        public void onDrawingFinish() {
            // This is when the Activity UI is completely drawn on the screen
            viewLoadTrace.stop();             
        }         
    });
  1. Execute o app novamente e filtre o logcat com "Métrica de rastreamento de geração de registros". Toque no botão LOAD ACTIVITY e procure registros como este:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉 Parabéns! Você mediu com sucesso o tempo de carregamento de uma atividade e informou esses dados ao Monitoramento de desempenho do Firebase. Vamos conferir a métrica registrada no console do Firebase mais adiante neste codelab.

Finalidade do FirstDrawListener

Na seção acima, registramos um FirstDrawListener. A finalidade de FirstDrawListener é medir quando o primeiro frame começou e terminou de ser desenhado.

Ele implementa o ViewTreeObserver.OnDrawListener e substitui o callback onDraw(), que é invocado quando a árvore de visualização está prestes a ser desenhada. Em seguida, ele encapsula o resultado para fornecer dois callbacks de utilidade, onDrawingStart() e onDrawingFinish().

O código completo do FirstDrawListener pode ser encontrado no código-fonte do codelab.

6. Medir o tempo de carregamento de um fragmento

Medir o tempo de carregamento de um Fragment é semelhante a como fazemos isso para uma Activity, mas com algumas pequenas diferenças. Vamos instrumentar um trace de código personalizado:

  1. Substitua o callback onAttach() e comece a gravar seu fragmentLoadTrace. Vamos chamar esse rastreamento de Test-Fragment-LoadTime.

Como explicado em uma etapa anterior, o objeto Fragment pode ser criado a qualquer momento, mas só fica ativo quando é anexado à atividade do host.

TestFragment.java

public class TestFragment extends Fragment {

   // TODO (1): Declare the Trace variable.
   private Trace fragmentLoadTrace;

   @Override
   public void onAttach(@NonNull Context context) {
       super.onAttach(context);

       // TODO (2): Start trace recording as soon as the Fragment is attached to its host Activity.
       fragmentLoadTrace = FirebasePerformance.startTrace("TestFragment-LoadTime");
   }
  1. Registre o FirstDrawListener no callback onViewCreated(). Em seguida, de maneira semelhante ao exemplo de atividade, pare o rastreamento no onDrawingFinish().

TestFragment.java

@Override
public void onViewCreated(@NonNull View mainView, Bundle savedInstanceState) {
   super.onViewCreated(mainView, savedInstanceState);

   // ...

   // TODO (3): Register the callback to listen for first frame rendering (see
   //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when view drawing is
   //  finished.
   FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {

       @Override
       public void onDrawingStart() {
           // In practice you can also record this event separately
       }

       @Override
       public void onDrawingFinish() {
           // This is when the Fragment UI is completely drawn on the screen
           fragmentLoadTrace.stop();
       }
   });
  1. Execute o app novamente e filtre o logcat com "Métrica de rastreamento de geração de registros". Toque no botão LOAD FRAGMENT e procure registros como este:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

🎉 Parabéns! Você mediu o tempo de carregamento de um fragmento e informou esses dados ao Monitoramento de desempenho do Firebase. Vamos conferir a métrica registrada no console do Firebase mais adiante neste codelab.

7. Entender a renderização de tela e o que é um frame lento/congelado

A renderização da interface é o ato de gerar um frame do seu app e mostrá-lo na tela. Para garantir que a interação do usuário com o app seja suave, ele precisa renderizar frames em menos de 16 ms para atingir 60 quadros por segundo ( por que 60 fps?). Se o app tiver uma renderização lenta da interface, o sistema será forçado a pular frames, e o usuário vai perceber a renderização lenta do app. Chamamos isso de instabilidade.

Da mesma forma, frames congelados são frames da interface que levam mais de 700 ms para serem renderizados. Esse atraso é um problema porque seu app parece estar travado e não responde à entrada do usuário por quase um segundo enquanto o frame está renderizando.

8. Medir os frames lentos/congelados de um fragmento

O Firebase Performance Monitoring captura automaticamente frames lentos/congelados de uma atividade apenas se ela for acelerada por hardware. No entanto, esse recurso não está disponível para fragmentos. Os frames lentos/congelados de um fragmento são definidos como os frames lentos/congelados de toda a atividade entre os callbacks onFragmentAttached() e onFragmentDetached() no ciclo de vida do fragmento.

Com base na motivação da classe AppStateMonitor (que faz parte do SDK do Monitoramento de desempenho e é responsável por gravar rastreamentos de tela para atividades), implementamos a classe ScreenTrace (que faz parte do repositório de código-fonte deste codelab). A classe ScreenTrace pode ser conectada ao callback do ciclo de vida FragmentManager da atividade para capturar frames lentos/congelados. Essa classe fornece duas APIs públicas:

  • recordScreenTrace(): inicia a gravação de um trace de tela.
  • sendScreenTrace(): interrompe a gravação de um rastreamento de tela e anexa métricas personalizadas para registrar as contagens de frames totais, lentos e congelados.

Ao anexar essas métricas personalizadas, os traces de tela para Fragments podem ser processados da mesma forma que os traces de tela para uma Activity e podem ser exibidos junto com outros traces de renderização de tela no painel Desempenho do console do Firebase.

Veja como registrar traces de tela para seu fragmento:

  1. Inicialize a classe ScreenTrace na atividade que hospeda o fragmento.

MainActivity.java

// Declare the Fragment tag
private static final String FRAGMENT_TAG = TestFragment.class.getSimpleName();

// TODO (1): Declare the ScreenTrace variable.
private ScreenTrace screenTrace;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // TODO (2): Initialize the ScreenTrace variable.
    screenTrace = new ScreenTrace(this, FRAGMENT_TAG);

    // ...
}
  1. Ao carregar o fragmento, registre FragmentLifecycleCallbacks e substitua os callbacks onFragmentAttached() e onFragmentDetached(). Fizemos isso para você. Você precisa iniciar a gravação de rastreamentos de tela no callback onFragmentAttached() e interromper a gravação no callback onFragmentDetached().

MainActivity.java

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
       new FragmentManager.FragmentLifecycleCallbacks() {

           @Override
           public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
               super.onFragmentAttached(fm, f, context);

               // TODO (3): Start recording the screen traces as soon as the Fragment is
               //  attached to its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.recordScreenTrace();
               }
           }

           @Override
           public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {
               super.onFragmentDetached(fm, f);

               // TODO (4): Stop recording the screen traces as soon as the Fragment is
               //  detached from its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.sendScreenTrace();
               }

               // Unregister Fragment lifecycle callbacks after the Fragment is detached
               fm.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
           }
       };
  1. Execute o app novamente e toque no botão LOAD FRAGMENT. Aguarde alguns segundos e clique em back button na barra de navegação inferior.

Filtre o logcat com Métrica de rastreamento de registros e procure registros como os abaixo:

I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)

Filtre o logcat com "FireperfViews" e procure registros como este:

D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX

🎉 Parabéns! Você mediu com sucesso os frames lentos/congelados de um fragmento e informou esses dados ao Monitoramento de desempenho do Firebase. Vamos conferir as métricas registradas no console do Firebase mais adiante neste codelab.

9. Verificar métricas no console do Firebase

  1. No logcat, clique no URL do console do Firebase para acessar a página de detalhes de um rastreamento. ceb9d5ba51bb6e89.jpeg

Outra opção é selecionar o projeto que tem seu app no console do Firebase. No painel à esquerda, localize a seção Lançamento e monitoramento e clique em Performance.

  • Na guia principal Painel, role a página para baixo até a tabela de traces e clique na guia Traces personalizados. Nesta tabela, você vai encontrar os traces de código personalizados que adicionamos antes, além de alguns traces prontos para uso, como o trace _app_start.
  • Encontre seus dois traces de código personalizado, TestActivity-LoadTime e TestFragment-LoadTime. Clique em Duração em qualquer um deles para ver mais detalhes sobre os dados coletados.

a0d8455c5269a590.png

  1. A página de detalhes do trace de código personalizado mostra informações sobre a duração do trace (ou seja, o tempo de carregamento medido).

5e92a307b7410d8b.png

  1. Você também pode conferir os dados de performance do rastreamento de tela personalizado.
  • Volte para a guia principal Painel, role para baixo até a tabela de rastreamentos e clique na guia Renderização da tela. Nesta tabela, você vai encontrar os rastreamentos de tela personalizados que adicionamos antes, além de rastreamentos de tela prontos para uso, como o rastreamento MainActivity.
  • Encontre seu rastreamento de tela personalizado, MainActivity-TestFragment. Clique no nome do trace para conferir os dados agregados de renderização lenta e frames congelados.

ee7890c7e2c28740.png

10. Parabéns

Parabéns! Você mediu com sucesso o tempo de carregamento e o desempenho de renderização da tela de uma atividade e de um fragmento usando o Monitoramento de desempenho do Firebase.

O que você realizou

A seguir

O Firebase Performance oferece mais maneiras de medir o desempenho do seu app além do rastreamento personalizado. Ele mede automaticamente o tempo de inicialização do app, o desempenho do app em primeiro plano e em segundo plano. Agora é hora de verificar essas métricas no Console do Firebase.

Além disso, o Firebase Performance oferece monitoramento automático de solicitações de rede HTTP/S. Assim, é possível instrumentar facilmente solicitações de rede sem escrever uma única linha de código. Você pode tentar enviar algumas solicitações de rede do seu app e encontrar as métricas no console do Firebase?

Bônus

Agora que você sabe como medir o tempo de carregamento e o desempenho de renderização de tela da sua atividade/fragmento usando traces de código personalizados, pode explorar nossa base de código de código aberto para ver se é possível capturar essas métricas imediatamente para qualquer atividade/fragmento que faça parte do app. Se quiser, pode enviar o PR :-)

11. Aprendizagem extra

Entender o que acontece durante o carregamento de uma atividade ajuda a entender melhor as características de desempenho do seu app. Em uma etapa anterior, descrevemos em alto nível o que acontece durante o carregamento de uma atividade, mas o diagrama a seguir descreve cada fase com muito mais detalhes.

cd61c1495fad7961.png