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 fundamental dos apps Android e afetam diretamente a experiência do usuário. Por exemplo, sua atividade ou fragmento contém a interface que contém os componentes de visualização com que os usuários interagem. Os usuários não conseguem ver todo o conteúdo da IU até que ele seja completamente desenhado na tela. Telas lentas e congeladas 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 oferece essas métricas de desempenho prontas para uso?

O Monitoramento de desempenho do Firebase captura automaticamente alguns dados de desempenho prontos para uso, como o horário de início do app (apenas o tempo de carregamento da primeira atividade) e o desempenho da renderização de tela (por exemplo, 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 as próprias visualizações personalizadas para casos de uso mais complexos. Por isso, é útil entender como medir o tempo de carregamento e o desempenho de renderização da tela de atividades e fragmentos instrumentando traces de código personalizados no app. É possível estender facilmente este codelab para medir o desempenho dos componentes de visualização personalizada.

O que você aprenderá

  • Como adicionar o Monitoramento de desempenho do Firebase a um app Android
  • Noções básicas sobre o carregamento de uma atividade ou de um fragmento
  • Como instrumentar traces de código personalizados para medir o tempo de carregamento de uma atividade ou um fragmento.
  • Noções básicas sobre renderização de tela e o que é um frame lento/congelado
  • Como instrumentar traces de código personalizados com métricas para gravar telas lentas/congeladas
  • Como conferir as métricas coletadas no console do Firebase

O que é necessário

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

2. Etapas da configuração

Buscar o código

Execute os comandos abaixo para clonar o exemplo de código para este 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. Provavelmente você vai encontrar alguns erros de compilação ou talvez 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 do Firebase Assistente para registrar nosso app Android com 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/Help > Verifique se há atualizações para confirmar se você está usando as versões mais recentes do Android Studio e do Firebase Assistente.
  2. Selecione Ferramentas > Firebase para abrir o painel do Assistente.

e791bed0999db1e0.png.

  1. Escolha Monitoramento de desempenho para adicionar ao app e clique em Começar a usar o Monitoramento de desempenho.
  2. Clique em Conectar ao Firebase para conectar seu projeto do Android ao Firebase (isso abrirá o console do Firebase no seu navegador).
  3. No Console do Firebase, clique em Adicionar projeto e digite o nome de um projeto do Firebase (se você já tiver um projeto do Firebase, selecione-o). Clique em Continuar e aceite os termos para criar o projeto do Firebase e um novo app do Firebase.
  4. Em seguida, uma caixa de diálogo vai aparecer para conectar o novo app do Firebase ao projeto do Android Studio.

42c498d28ead2b77.png

  1. No painel Assistente do Android Studio, você verá a confirmação de que o 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 app.

Uma caixa de diálogo Accept Changes vai aparecer. Depois disso, o Android Studio vai sincronizar o app para garantir que todas as dependências necessárias tenham sido adicionadas.

9b58145acc4be030.png.

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

aa0d46fc944e0c0b.png

Como 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. Execute o aplicativo

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

O app tem dois botões que levam você a uma atividade e um fragmento correspondentes, como este:

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. Noções básicas sobre o carregamento de uma atividade ou um fragmento

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

Noções básicas sobre o carregamento de uma atividade

Para uma atividade, o tempo de carregamento é definido como o tempo que começa quando o objeto da atividade é criado até que o primeiro frame seja totalmente renderizado na tela (é quando o usuário vai ver a interface completa da atividade pela primeira vez). Para medir se o app está totalmente renderizado, use o método reportFullyDrawn() para medir o tempo decorrido entre a inicialização do aplicativo e a exibição completa de todos os recursos e das hierarquias de visualização.

De modo geral, quando o app chama startActivity(Intent), o sistema executa automaticamente os processos a seguir. Cada processo leva tempo para ser concluído, o que aumenta o tempo entre a criação da atividade e quando o usuário vê a interface da atividade 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 que começa quando o fragmento é anexado à atividade host até que o primeiro frame da visualização do fragmento seja totalmente renderizado na tela.

5. Medir o tempo de carregamento de uma atividade

Como os atrasos no primeiro frame podem atrapalhar a experiência do usuário, é importante entender o atraso no carregamento inicial. É 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 de atividade assim que o objeto de atividade for criado.

TestActivity.java (em inglês)

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 adicione a visualização 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 o desempenho dela). Registre o FirstDrawListener no final do callback onCreate() da atividade. Interrompa 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. Em seguida, filtre o logcat com Métrica de rastreamento de registro. Toque no botão LOAD ACTIVITY e procure registros como abaixo:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉 Parabéns! Você mediu o tempo de carregamento de uma atividade e informou os dados ao Monitoramento de desempenho do Firebase. Veremos a métrica gravada no Console do Firebase mais adiante neste codelab.

Finalidade do FirstDrawListener

Na seção acima, registramos um FirstDrawListener. O objetivo da 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 renderizada. Em seguida, ele envolve o resultado para fornecer dois callbacks de utilitário onDrawingStart() e onDrawingFinish().

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

6. Medir o tempo de carregamento de um fragmento

A medição do tempo de carregamento de um fragmento é semelhante à medição de uma atividade, mas com algumas pequenas diferenças. Novamente, instrumentaremos um trace de código personalizado:

  1. Substitua o callback onAttach() e comece a gravar o fragmentLoadTrace. Vamos nomear esse trace como Test-Fragment-LoadTime.

Conforme explicado em uma etapa anterior, o objeto Fragment pode ser criado a qualquer momento, mas fica ativo somente quando está anexado à atividade 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 forma semelhante ao exemplo da atividade, pare o rastro no onDrawingFinish().

TestFragment.java (em inglês)

@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. Em seguida, filtre o logcat com Métrica de rastreamento de registro. 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. Noções básicas sobre 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 ignorar frames, e o usuário vai perceber a renderização lenta do app. Chamamos isso de instabilidade.

Da mesma forma, os frames congelados são frames da interface que levam mais de 700 ms para renderização. Esse atraso é um problema porque o 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 Monitoramento de desempenho do Firebase captura automaticamente frames lentos/congelados de uma atividade (mas somente se ela for acelerada por hardware). No entanto, esse recurso não está disponível para fragmentos no momento. 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 a motivação da classe AppStateMonitor, que faz parte do SDK do Monitoramento de desempenho responsável por gravar traces de tela para a atividade, implementamos a classe ScreenTrace (que faz parte deste repositório de código-fonte do 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(): começa a gravar um trace de tela
  • sendScreenTrace(): interrompe a gravação de um trace de tela e anexa métricas personalizadas para registrar contagens de frames totais, lentos e congelados.

Ao anexar essas métricas personalizadas, os rastros de tela para fragmentos podem ser processados da mesma forma que os rastros de tela para uma atividade e podem ser exibidos com outros rastros de renderização de tela no painel Performance do console do Firebase.

Veja como registrar rastros 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 seu fragmento, registre-se em FragmentLifecycleCallbacks e substitua os callbacks onFragmentAttached() e onFragmentDetached(). Fizemos isso para você. Você precisa iniciar a gravação de rastros de tela no callback onFragmentAttached() e parar a gravação no callback onFragmentDetached().

MainActivity.java (em inglês)

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. Em seguida, toque no botão LOAD FRAGMENT. Aguarde alguns segundos e clique em back button na barra de navegação na parte de baixo.

Filtre o logcat com Métrica de registro de trace e procure registros como este:

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 os frames lentos/congelados de um fragmento e informou esses dados ao Monitoramento de desempenho do Firebase. Veremos as métricas gravadas 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 trace. ceb9d5ba51bb6e89.jpeg

Como alternativa, no Console do Firebase, selecione o projeto que tem seu app. No painel à esquerda, localize a seção Liberação e monitoramento e clique em Performance.

  • Na guia principal Painel, role a tela para baixo até a tabela de traces e clique na guia Traces personalizados. Nesta tabela, você vai encontrar os rastros de código personalizados que adicionamos anteriormente, além de alguns traces prontos para uso, como o trace _app_start.
  • Encontre os dois traces de código personalizados, TestActivity-LoadTime e TestFragment-LoadTime. Clique em Duração em qualquer uma delas para conferir 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 desempenho do seu rastreamento de tela personalizado.
  • Volte para a guia principal Painel, role para baixo até a tabela de rastros e clique na guia Renderização de tela. Nesta tabela, você vai encontrar os rastros de tela personalizados que adicionamos anteriormente, além de rastros de tela prontos para uso, como o rastro MainActivity.
  • Encontre o rastro de tela personalizado, MainActivity-TestFragment. Clique no nome do trace para ver os dados agregados de renderização lenta e frames congelados.

ee7890c7e2c28740.png

10. Parabéns

Parabéns! Você mediu o tempo de carregamento e o desempenho da renderização de tela de uma atividade e de um fragmento com 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 e os dados de desempenho do app em primeiro e em segundo plano. É 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. Com isso, é possível instrumentar facilmente as solicitações de rede sem precisar 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, confira nossa base de código aberto para saber se é possível capturar essas métricas prontas para uso em qualquer atividade/fragmento que faça parte do app. Se quiser, pode mandar o RP :-)

11. Aprendizagem extra

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

cd61c1495fad7961.png