Medir o tempo de carregamento e a renderização de 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 aplicativos Android e afetam diretamente a experiência do usuário. Por exemplo, a atividade ou o fragmento contém a interface que contém os componentes de visualização com que os usuários interagem. Os usuários não podem ver todo o conteúdo da interface até que ela seja completamente renderizada na tela. Telas lentas e congeladas prejudicam diretamente a interação do usuário com o app e criam uma experiência ruim para o usuário.

O Monitoramento de desempenho do Firebase não oferece essas métricas 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 (ou seja, o tempo de carregamento da primeira atividade somente) e o desempenho da renderização de tela (por exemplo, frames lentos e congelados em atividades, mas não em fragmentos). No entanto, os apps do setor geralmente não têm muitas atividades, mas sim uma e vários fragmentos. Além disso, muitos apps geralmente implementam as próprias visualizações personalizadas para casos de uso mais complexos. Portanto, é ú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. Você pode 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 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 registrar telas lentas/congeladas.
  • Como visualizar 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 deste codelab. Uma pasta chamada codelab-measure-android-view-performance será criada 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 máquina, faça o download do código diretamente no GitHub.

Importe o projeto measure-view-performance-start para o Android Studio. Você provavelmente vai notar 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, usaremos 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 projeto do Android. Tudo isso sem sair do Android Studio.

Conectar seu app ao Firebase

  1. Acesse Android Studio/Help > Check for updates para confirmar se você está usando as versões mais recentes do Android Studio e do Firebase Assistente.
  2. Selecione Tools > Firebase para abrir o painel Assistente.

e791bed0999db1e0.png

  1. Escolha Monitoramento de desempenho para adicionar ao app e clique em Introdução ao Monitoramento de desempenho.
  2. Clique em Conectar ao Firebase para conectar seu projeto do Android ao Firebase. Isso abre o Console do Firebase no seu navegador.
  3. No Console do Firebase, clique em Adicionar projeto e insira 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 e um novo app do Firebase.
  4. Em seguida, você verá uma caixa de diálogo para Conectar seu 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 seu app está conectado ao Firebase.

dd8bdd9488167a0.png

Adicionar o Monitoramento de desempenho ao app

No painel Assistant no Android Studio, clique em Add Performance Monitoring to your app.

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

9b58145acc4be030.png

Por fim, uma mensagem de êxito vai aparecer no painel Assistant do Android Studio informando que todas as dependências estão configuradas corretamente.

aa0d46fc944e0c0b.png

Como uma etapa extra, ative a geração de registros de depuração seguindo as instruções em "(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 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 etapas a seguir deste codelab, você aprenderá a medir o tempo de carregamento e o desempenho da renderização de 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 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 período a partir do momento em que o objeto da atividade foi criado até que o Primeiro frame seja completamente desenhado na tela. Esse é o momento em que o usuário verá a IU completa da atividade pela primeira vez. Para avaliar se o app está totalmente carregado, 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 das 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 o tempo entre a criação da atividade e o momento em que o usuário vê a interface dela na tela.

c20d14b151549937.png

Noções básicas sobre o carregamento de um fragmento

Semelhante à atividade, o tempo de carregamento de um fragmento é definido como o período entre o momento em que o fragmento é anexado à atividade host e o primeiro frame da visualização do fragmento ser completamente desenhado na tela.

5. Medir o tempo de carregamento de uma atividade

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

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

TestActivity.java (link 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. Modifique o callback onCreate() e acesse 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 para saber mais sobre FirstDrawListener e o que pode afetar a performance dele. Registre o FirstDrawListener ao final do callback onCreate() da atividade. Interrompa o viewLoadTrace no callback onDrawingFinish().

TestActivity.java (link em inglês)

    // 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 "Logging trace metric". Toque no botão LOAD ACTIVITY e procure registros como estes:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

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

Finalidade do FirstDrawListener

Na seção acima, registramos um FirstDrawListener. O objetivo do FirstDrawListener é medir quando o primeiro frame começou e terminou de mostrar.

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

O código completo de FirstDrawListener pode ser encontrado neste código-fonte do 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, vamos instrumentar um trace de código personalizado:

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

Conforme explicado em uma etapa anterior, o objeto do fragmento pode ser criado a qualquer momento, mas só se torna ativo quando está anexado à atividade do host.

TestFragment.java (em inglês)

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 da atividade, interrompa 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 "Logging trace metric". Toque no botão LOAD FRAGMENT e procure registros como estes:
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 gravada 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 pular frames, e o usuário vai notar oscilações no 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 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 para uma atividade (mas somente se for com aceleração de 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 registrar os traces de tela da atividade, implementamos a classe ScreenTrace, que faz parte deste repositório do código-fonte do codelab. A classe ScreenTrace pode ser vinculada 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 rastro 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 traces de tela para fragmentos podem ser processados da mesma forma que os traces de uma atividade e exibidos com outros traces de renderização de tela no painel Desempenho do Console do Firebase.

Veja como registrar rastreamentos de tela para seu fragmento:

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

MainActivity.java (link em inglês)

// 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 modifique os callbacks onFragmentAttached() e onFragmentDetached(). Fizemos isso para você. É necessário começar a gravar traces de tela no callback onFragmentAttached() e parar a gravação no callback onFragmentDetached().

MainActivity.java (link 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 no back button na barra de navegação inferior.

Filtre o logcat com "Logging trace metric" e procure registros como estes:

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

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

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. 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 trace. ceb9d5ba51bb6e89.jpeg

Como alternativa, no Console do Firebase, selecione o projeto que contém seu app. No painel à esquerda, localize a seção Versões e monitorar e clique em Desempenho.

  • Na guia Painel principal, role 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 anteriormente, além de alguns traces prontos para uso, como o trace _app_start.
  • Encontre seus dois traces de código personalizados, TestActivity-LoadTime e TestFragment-LoadTime. Clique na Duração de 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. Também é possível conferir os dados de performance do trace de tela personalizado.
  • Volte para a guia Painel principal, role para baixo até a tabela de traces e clique na guia Renderização de tela. Nesta tabela, você vai encontrar os traces de tela personalizados que adicionamos anteriormente, além de todos os traces de tela prontos para uso, como o MainActivity.
  • Encontre seu trace de tela personalizado, MainActivity-TestFragment. Clique no nome do trace para visualizar 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 um fragmento usando o Monitoramento de desempenho do Firebase.

Suas conquistas

A seguir

O Firebase Performance oferece mais formas de medir o desempenho do seu app, além do trace personalizado. Ela mede automaticamente o tempo de inicialização do app e os dados de desempenho de apps em primeiro plano e em segundo plano. É hora de conferir 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 as solicitações de rede facilmente sem escrever uma linha sequer de código. Você pode tentar enviar algumas solicitações de rede pelo 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 consegue capturar essas métricas prontas para qualquer atividade/fragmento que faça parte do app? Fique à vontade para enviar o PR se quiser :-)

11. Aprendizado bônus

Entender o que está acontecendo durante o carregamento de uma atividade ajuda a entender melhor as características de desempenho do 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