Mide el tiempo de carga y la renderización de la pantalla con Firebase Performance Monitoring

1. Introducción

Última actualización: 11/03/2021

¿Por qué necesitamos medir el rendimiento de las Vistas?

Las vistas son una parte clave de las aplicaciones para Android que influyen directamente en la experiencia del usuario. Por ejemplo, tu Activity o Fragment contiene la IU que incluye los componentes de View con los que interactúan los usuarios. Los usuarios no pueden ver todo el contenido de la IU hasta que se dibuja por completo en la pantalla. Las pantallas lentas y congeladas perjudicarán directamente la interacción del usuario con tu app y crearán una mala experiencia del usuario.

¿Firebase Performance Monitoring no proporciona estas métricas de rendimiento de forma predeterminada?

Firebase Performance Monitoring captura automáticamente algunos datos de rendimiento listos para usar, como el tiempo de inicio de tu app (es decir, el tiempo de carga solo de tu primera actividad) y el rendimiento del procesamiento de pantalla (es decir, fotogramas lentos y congelados para las actividades, pero no para los fragmentos). Sin embargo, las apps de la industria no suelen tener muchas actividades, sino una actividad y varios fragmentos. Además, muchas apps suelen implementar sus propias vistas personalizadas para casos de uso más complejos. Por lo tanto, suele ser útil comprender cómo medir el tiempo de carga y el rendimiento de la renderización de la pantalla de las actividades y los fragmentos instrumentando seguimientos de código personalizados en tu app. Puedes extender fácilmente este codelab para medir el rendimiento de los componentes de la vista personalizada.

Qué aprenderás

  • Cómo agregar Firebase Performance Monitoring a una app para Android
  • Cómo comprender la carga de una actividad o un fragmento
  • Cómo instrumentar seguimientos de código personalizado para medir el tiempo de carga de una actividad o un fragmento
  • Comprensión de la renderización de pantalla y qué es un fotograma lento o congelado
  • Cómo instrumentar seguimientos de código personalizados con métricas para registrar pantallas lentas o congeladas
  • Cómo ver las métricas recopiladas en Firebase console

Requisitos

  • Android Studio 4.0 o una versión posterior
  • Un dispositivo o emulador de Android
  • Java 8 o una versión posterior

2. Cómo prepararte

Obtén el código

Ejecuta los siguientes comandos para clonar el código de muestra de este codelab. Se creará una carpeta llamada codelab-measure-android-view-performance en tu máquina:

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

Si no tienes git en tu máquina, también puedes descargar el código directamente desde GitHub.

Importa el proyecto measure-view-performance-start en Android Studio. Es probable que veas algunos errores de compilación o tal vez una advertencia sobre un archivo google-services.json faltante. Corregiremos esto en la siguiente sección de este paso.

En este codelab, usaremos el complemento Firebase Assistant para registrar nuestra app para Android con un proyecto de Firebase y agregar los archivos de configuración, los complementos y las dependencias de Firebase necesarios a nuestro proyecto de Android, todo desde Android Studio.

Conecta tu app a Firebase

  1. Ve a Android Studio/Ayuda > Buscar actualizaciones para asegurarte de estar usando las versiones más recientes de Android Studio y Firebase Assistant.
  2. Selecciona Herramientas > Firebase para abrir el panel Asistente.
    e791bed0999db1e0.png
  3. Elige Performance Monitoring para agregar a tu app y, luego, haz clic en Comienza a usar Performance Monitoring.
  4. Haz clic en el botón para crear un proyecto nuevo y, luego, ingresa un nombre (por ejemplo, Measure Performance Codelab).
  5. Haz clic en Continuar.
  6. Si se te solicita, revisa y acepta las Condiciones de Firebase y, luego, haz clic en Continuar.
  7. (Opcional) Habilita la asistencia de IA en Firebase console (llamada "Gemini en Firebase").
  8. Para este codelab, no necesitas Google Analytics, por lo que debes desactivar la opción de Google Analytics.
  9. A continuación, deberías ver un diálogo para conectar tu nueva app de Firebase a tu proyecto de Android Studio.
    42c498d28ead2b77.png
  10. De vuelta en Android Studio, en el panel Assistant, deberías ver la confirmación de que tu app está conectada a Firebase.
    dda8bdd9488167a0.png

Agrega Performance Monitoring a la app

En el panel Assistant de Android Studio, haz clic en Add Performance Monitoring to your app.

Deberías ver un diálogo para Aceptar cambios, después de lo cual Android Studio debería sincronizar tu app para garantizar que se hayan agregado todas las dependencias necesarias.

9b58145acc4be030.png

Por último, deberías ver el mensaje de éxito en el panel Assistant de Android Studio que indica que todas las dependencias se configuraron correctamente.

aa0d46fc944e0c0b.png

Como paso adicional, habilita el registro de depuración siguiendo las instrucciones del paso "(Opcional) Habilita el registro de depuración". Las mismas instrucciones también están disponibles en la documentación pública.

3. Ejecuta la app

Si integraste correctamente tu app con el SDK de Performance Monitoring, el proyecto debería compilarse. En Android Studio, haz clic en Run > Run ‘app’ para compilar y ejecutar la app en el dispositivo o emulador de Android conectado.

La app tiene dos botones que te llevan a una actividad y un fragmento correspondientes, como se muestra a continuación:

410d8686b4f45c33.png

En los siguientes pasos de este codelab, aprenderás a medir el tiempo de carga y el rendimiento de la renderización de la pantalla de tu actividad o fragmento.

4. Cómo comprender la carga de una actividad o un fragmento

En este paso, aprenderemos qué hace el sistema durante la carga de una actividad o un fragmento.

Cómo comprender la carga de una actividad

En el caso de una Activity, el tiempo de carga se define como el tiempo que transcurre desde que se crea el objeto Activity hasta que el primer fotograma se dibuja por completo en la pantalla (este es el momento en que el usuario verá la IU completa de la Activity por primera vez). Para medir si tu app se dibujó por completo, puedes usar el método reportFullyDrawn() para medir el tiempo transcurrido entre el inicio de la app y la visualización completa de todos los recursos y las jerarquías de vistas.

En un nivel alto, cuando tu app llama a startActivity(Intent), el sistema realiza automáticamente los siguientes procesos. Cada proceso lleva tiempo para completarse, lo que se suma a la duración entre la creación de la actividad y el momento en que el usuario ve la IU de la actividad en su pantalla.

c20d14b151549937.png

Información sobre la carga de un fragmento

Al igual que en el caso de la Activity, el tiempo de carga de un Fragment se define como el tiempo que transcurre desde que el Fragment se adjunta a su Activity host hasta que el primer fotograma de la vista del Fragment se dibuja por completo en la pantalla.

5. Cómo medir el tiempo de carga de una actividad

Los retrasos en el primer fotograma pueden generar una mala experiencia del usuario, por lo que es importante comprender cuánto retraso de carga inicial experimentan tus usuarios. Puedes instrumentar un seguimiento de código personalizado para medir este tiempo de carga:

  1. Inicia el registro de código personalizado (llamado TestActivity-LoadTime) en la clase Activity tan pronto como se cree el objeto Activity.

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. Anula la devolución de llamada onCreate() y obtén la View agregada por el 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. Incluimos una implementación de FistDrawListener, que tiene dos devoluciones de llamada: onDrawingStart() y onDrawingFinish() (consulta la siguiente sección para obtener más detalles sobre FirstDrawListener y qué puede afectar su rendimiento). Registra el FirstDrawListener al final de la devolución de llamada onCreate() de la actividad. Debes detener tu viewLoadTrace en la devolución de llamada de 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. Vuelve a ejecutar la app y, luego, filtra el registro de logcat con "Logging trace metric". Presiona el botón LOAD ACTIVITY y busca registros como los que se muestran a continuación:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉 ¡Felicitaciones! Mediste correctamente el tiempo de carga de una actividad y enviaste esos datos a Firebase Performance Monitoring. Más adelante en este codelab, veremos la métrica registrada en Firebase console.

Propósito de FirstDrawListener

En la sección anterior, registramos un FirstDrawListener. El propósito de FirstDrawListener es medir cuándo comenzó y finalizó el dibujo del primer fotograma.

Implementa ViewTreeObserver.OnDrawListener y anula la devolución de llamada onDraw() que se invoca cuando el árbol de View está a punto de dibujarse. Luego, encapsula el resultado para proporcionar dos devoluciones de llamada de utilidad onDrawingStart() y onDrawingFinish().

El código completo de FirstDrawListener se puede encontrar en el código fuente de este codelab.

6. Cómo medir el tiempo de carga de un fragmento

Medir el tiempo de carga de un fragmento es similar a cómo lo medimos para una actividad, pero con algunas diferencias menores. Una vez más, instrumentaremos un seguimiento de código personalizado:

  1. Anula la devolución de llamada onAttach() y comienza a grabar tu fragmentLoadTrace. Llamaremos a este registro Test-Fragment-LoadTime.

Como se explicó en un paso anterior, el objeto Fragment se puede crear en cualquier momento, pero se activa solo cuando se adjunta a su Activity de 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. Registra el FirstDrawListener en la devolución de llamada onViewCreated(). Luego, de manera similar al ejemplo de Activity, detén el registro en 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. Vuelve a ejecutar la app y, luego, filtra el registro de logcat con "Logging trace metric". Presiona el botón LOAD FRAGMENT y busca registros como los que se muestran a continuación:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

🎉 ¡Felicitaciones! Mediste correctamente el tiempo de carga de un fragmento y enviaste esos datos a Firebase Performance Monitoring. Más adelante en este codelab, veremos la métrica registrada en Firebase console.

7. Comprensión de la renderización de pantalla y qué es un fotograma lento o congelado

La renderización de la IU consiste en generar un fotograma desde tu app y mostrarlo en la pantalla. Para garantizar que la interacción del usuario con tu app sea fluida, esta debe renderizar los fotogramas en menos de 16 ms para lograr 60 fotogramas por segundo ( ¿Por qué 60 FPS?). Si tu app tiene una renderización lenta de la IU, el sistema se verá obligado a omitir fotogramas, y el usuario notará un salto en tu app. Esto se denomina bloqueo.

Del mismo modo, los fotogramas congelados son fotogramas de la IU que tardan más de 700 ms en renderizarse. Este retraso es un problema, ya que tu app parece estar bloqueada y no responde a las entradas que realiza el usuario durante casi un segundo completo mientras se renderiza el fotograma.

8. Cómo medir los fotogramas lentos o congelados de un fragmento

Firebase Performance Monitoring captura automáticamente los fotogramas lentos o inactivos de una actividad (pero solo si tiene aceleración de hardware). Sin embargo, esta función no está disponible para los fragmentos por el momento. Los fotogramas lentos o congelados de un fragmento se definen como los fotogramas lentos o congelados de toda la actividad entre las devoluciones de llamada onFragmentAttached() y onFragmentDetached() en el ciclo de vida del fragmento.

Tomando como motivación la clase AppStateMonitor (que forma parte del SDK de Performance Monitoring y es responsable de registrar los registros de pantalla para Activity), implementamos la clase ScreenTrace (que forma parte de este repositorio de código fuente del codelab). La clase ScreenTrace se puede conectar a la devolución de llamada del ciclo de vida de FragmentManager de la actividad para capturar fotogramas lentos o inactivos. Esta clase proporciona dos APIs públicas:

  • recordScreenTrace(): Inicia la grabación de un registro de pantalla.
  • sendScreenTrace(): Detiene la grabación de un registro de pantalla y adjunta métricas personalizadas para registrar los recuentos de fotogramas totales, lentos y congelados.

Si adjuntas estas métricas personalizadas, los seguimientos de pantalla de los fragmentos se pueden controlar de la misma manera que los seguimientos de pantalla de una actividad y se pueden mostrar junto con otros seguimientos de renderización de pantalla en el panel Rendimiento de Firebase console.

A continuación, te indicamos cómo registrar seguimientos de pantalla para tu Fragment:

  1. Inicializa la clase ScreenTrace en la actividad que aloja el 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. Cuando cargues tu Fragment, regístrate para FragmentLifecycleCallbacks y anula las devoluciones de llamada onFragmentAttached() y onFragmentDetached(). Ya lo hicimos por ti. Debes comenzar a registrar los seguimientos de pantalla en la devolución de llamada onFragmentAttached() y detener el registro en la devolución de llamada 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. Vuelve a ejecutar la app y, luego, presiona el botón LOAD FRAGMENT. Espera unos segundos y, luego, haz clic en back button en la barra de navegación inferior.

Filtra el logcat con "Logging trace metric" y, luego, busca registros como los que se muestran a continuación:

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

Filtra Logcat con "FireperfViews" y, luego, busca registros como los que se muestran a continuación:

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

🎉 ¡Felicitaciones! Mediste correctamente los fotogramas lentos o congelados de un fragmento y enviaste esos datos a Firebase Performance Monitoring. Más adelante en este codelab, veremos las métricas registradas en Firebase console.

9. Verifica las métricas en Firebase console

  1. En logcat, haz clic en la URL de Firebase console para visitar la página de detalles de un registro. ceb9d5ba51bb6e89.jpeg

Como alternativa, en Firebase console, selecciona el proyecto que tiene tu app. En el panel izquierdo, busca la sección Release & Monitor y, luego, haz clic en Performance.

  • En la pestaña principal Panel, desplázate hacia abajo hasta la tabla de registros y, luego, haz clic en la pestaña Registros personalizados. En esta tabla, verás los registros de seguimiento de código personalizado que agregamos antes, además de algunos registros listos para usar, como el registro _app_start.
  • Busca tus dos registros de seguimiento de código personalizado, TestActivity-LoadTime y TestFragment-LoadTime. Haz clic en la Duración de cualquiera de los dos para ver más detalles sobre los datos recopilados.

a0d8455c5269a590.png

  1. En la página de detalles del registro de código personalizado, se muestra información sobre la duración del registro (es decir, el tiempo de carga medido).

5e92a307b7410d8b.png

  1. También puedes ver los datos de rendimiento de tu registro de pantalla personalizado.
  • Regresa a la pestaña principal Panel, desplázate hacia abajo hasta la tabla de registros y, luego, haz clic en la pestaña Renderización de pantalla. En esta tabla, verás los registros de pantalla personalizados que agregamos antes, además de los registros de pantalla listos para usar, como el registro MainActivity.
  • Busca tu registro de pantalla personalizado, MainActivity-TestFragment. Haz clic en el nombre del registro para ver los datos agregados de los fotogramas congelados y de procesamiento lento.

ee7890c7e2c28740.png

10. Felicitaciones

¡Felicitaciones! Mediste correctamente el tiempo de carga y el rendimiento del procesamiento de pantalla de una actividad y un fragmento con Firebase Performance Monitoring.

Qué lograste

¿Qué sigue?

Firebase Performance proporciona más formas de medir el rendimiento de tu app además del registro personalizado. Mide automáticamente los datos de rendimiento sobre el tiempo de inicio de la app y las apps en primer y segundo plano. Es hora de que verifiques estas métricas en Firebase console.

Además, Firebase Performance ofrece supervisión automática de solicitudes de red HTTP/S. Con eso, puedes instrumentar fácilmente las solicitudes de red sin escribir una sola línea de código. ¿Puedes intentar enviar algunas solicitudes de red desde tu app y encontrar las métricas en Firebase console?

Contenido adicional

Ahora que sabes cómo medir el tiempo de carga y el rendimiento del procesamiento de pantalla de tu actividad o fragmento con seguimientos de código personalizados, ¿puedes explorar nuestra base de código de código abierto para ver si puedes capturar esas métricas de forma predeterminada para cualquier actividad o fragmento que forme parte de la app? Si quieres, puedes enviar la PR :-)

11. Aprendizaje adicional

Comprender lo que sucede durante la carga de una actividad te ayudará a comprender mejor las características de rendimiento de tu app. En un paso anterior, describimos de forma general lo que sucede durante la carga de una actividad, pero el siguiente diagrama describe cada fase con mucho más detalle.

cd61c1495fad7961.png