Perfilado basado en activadores

En Android 16 (nivel de API 36) y versiones posteriores, ProfilingManager te permite capturar perfiles automáticamente en respuesta a activadores del sistema, como errores de Aplicación no responde (ANR), fallas por memoria insuficiente (OutOfMemoryError) o eventos de inicio de la app. Como no puedes predecir cuándo ocurrirán muchos de estos eventos en el campo, no es práctico iniciar una grabación de forma manual con anticipación. Con la generación de perfiles basada en activadores, el sistema administra la captura y entrega el perfil resultante a tu app para que puedas hacer lo siguiente:

  • Depura problemas de estabilidad: Diagnostica errores de ANR impredecibles y fugas de memoria.
  • Optimiza los recorridos críticos del usuario (CUJ): Analiza flujos como el inicio de la app (TRIGGER_TYPE_COLD_START y TRIGGER_TYPE_APP_FULLY_DRAWN).
  • Comprende el comportamiento de los usuarios: Inspecciona eventos, como las salidas de la app iniciadas por el usuario.

Para obtener la lista completa de activadores disponibles, consulta ProfilingTrigger.

Tipos de activadores

Los activadores difieren en la forma en que el sistema captura el perfil cuando se produce el evento.

Activadores que usan el seguimiento en segundo plano

Muchos activadores, como un error de ANR, son la consecuencia de un problema anterior. Si inicias un registro solo después de que el sistema detecta el error de ANR, se perderá el trabajo de bloqueo que lo causó. Para capturar la actividad que lleva al evento, los siguientes activadores guardan una instantánea del seguimiento en segundo plano activo del sistema. Si no se está ejecutando ningún seguimiento en segundo plano cuando se produce el evento, no se recopila ningún perfil:

Activadores que inician una nueva captura

Los siguientes activadores no dependen de la traza en segundo plano. En cambio, el sistema inicia una nueva captura de perfil cuando se produce el evento:

Activadores en los que el comportamiento de captura varía según la anomalía

Para los siguientes activadores de anomalías, el tipo de perfil capturado (guardar una instantánea del seguimiento en segundo plano activo o iniciar una captura nueva) y el comportamiento de limitación de frecuencia dependen de la anomalía específica que detecte el sistema:

Configura un activador

En el siguiente código, se muestra cómo registrar el activador TRIGGER_TYPE_APP_FULLY_DRAWN y establecer un período de inactividad en él.

Kotlin

@RequiresApi(Build.VERSION_CODES.BAKLAVA)
fun recordWithTrigger() {
    val profilingManager =
        applicationContext.getSystemService(ProfilingManager::class.java) ?: return

    val triggers = ArrayList<ProfilingTrigger>()

    val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)
        .setRateLimitingPeriodHours(1)

    triggers.add(triggerBuilder.build())

    val callbackExecutor: Executor = Executors.newSingleThreadExecutor()

    val resultCallback = Consumer<ProfilingResult> { profilingResult ->
        if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
            Log.d(
                "ProfileTest",
                "Received profiling result file=" + profilingResult.resultFilePath
            )
            setupProfileUploadWorker(profilingResult.resultFilePath)
        } else {
            Log.e(
                "ProfileTest",
                "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage
            )
        }
    }

    profilingManager.registerForAllProfilingResults(callbackExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

    Choreographer.getInstance().postFrameCallback { frameTimeNanos ->
        // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted.
        reportFullyDrawn()
    }
}

Java

public void recordWithTrigger() {
  ProfilingManager profilingManager = getApplicationContext().getSystemService(
      ProfilingManager.class);
  List<ProfilingTrigger> triggers = new ArrayList<>();
  ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder(
      ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN);
  triggerBuilder.setRateLimitingPeriodHours(1);
  triggers.add(triggerBuilder.build());

  Executor callbackExecutor = Executors.newSingleThreadExecutor();
  Consumer<ProfilingResult> resultCallback =
      new Consumer<ProfilingResult>() {
        @Override
        public void accept(ProfilingResult profilingResult) {
          if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) {
            Log.d(
                "ProfileTest",
                "Received profiling result file=" + profilingResult.getResultFilePath());
            setupProfileUploadWorker(profilingResult.getResultFilePath());
          } else {
            Log.e(
                "ProfileTest",
                "Profiling failed errorcode="
                    + profilingResult.getErrorCode()
                    + " errormsg="
                    + profilingResult.getErrorMessage());
          }
        }
      };
  profilingManager.registerForAllProfilingResults(callbackExecutor, resultCallback);
  profilingManager.addProfilingTriggers(triggers);

  Choreographer.getInstance().postFrameCallback((f) -> {
    // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted.
    reportFullyDrawn();
  });
}

El código realiza estos pasos:

  1. Obtén el administrador: Recupera el servicio ProfilingManager.
  2. Define un activador: Compila un ProfilingTrigger para TRIGGER_TYPE_APP_FULLY_DRAWN. Este evento ocurre cuando la app llama a reportFullyDrawn() durante un inicio en frío iniciado por la actividad para informar que finalizó el inicio y es interactiva.
  3. Establece un período de inactividad del activador: Aplica un período de inactividad de 1 hora a este activador específico con setRateLimitingPeriodHours, además del limitador de frecuencia del sistema. Este tiempo de espera evita que la app registre más de un perfil de inicio por hora.
  4. Registra el objeto de escucha: Llama a registerForAllProfilingResults para definir la devolución de llamada que controla el resultado. Esta devolución de llamada recibe la ruta de acceso del perfil guardado a través de getResultFilePath().
  5. Agrega activadores: Registra la lista de activadores con ProfilingManager usando addProfilingTriggers.
  6. Evento de activación: Publica una devolución de llamada de fotograma Choreographer que llama a reportFullyDrawn() una vez que se dibuja el siguiente fotograma, lo que emite el evento TRIGGER_TYPE_APP_FULLY_DRAWN al sistema y activa una recopilación de perfiles, suponiendo que se estaba ejecutando un seguimiento en segundo plano del sistema y que la cuota del limitador de frecuencia está disponible. Para que se produzca este activador, asegúrate de que tu app llame a reportFullyDrawn().

Ciclo de vida de los activadores

Los activadores registrados persisten entre los reinicios de la app y del sistema. No es necesario que vuelvas a registrar los activadores cada vez que se inicia la app o después de un reinicio del sistema. Una vez que registras los activadores con addProfilingTriggers, el sistema los mantiene automáticamente para tu app hasta que los quites o se desinstale la app.

Cuándo cancelar el registro de activadores

Debido a que los activadores registrados persisten en los reinicios de la app y del sistema, omitir addProfilingTriggers en los lanzamientos posteriores no detiene los activadores registrados en una sesión anterior. Para dejar de capturar perfiles de eventos específicos o de todos los eventos, llama de forma explícita a removeProfilingTriggersByType o clearProfilingTriggers.

Cancela el registro de los activadores en las siguientes situaciones:

  • Segmentación de cohortes o reversión de marcas remotas: Puedes segmentar la creación de perfiles en las cohortes con la configuración remota o las marcas de pruebas A/B. Cada vez que un dispositivo abandone la cohorte de muestra o se desactive un parámetro, cancela el registro de los activadores.
  • Finalización de una campaña de depuración: Una vez que recopiles suficientes perfiles para diagnosticar un problema o enviar una corrección, quita el activador. Cancelar el registro de los activadores innecesarios conserva el almacenamiento del dispositivo y la cuota del limitador de frecuencia de tu app para otras solicitudes de generación de perfiles.

Ciclo de vida de la devolución de llamada

A diferencia de las solicitudes de generación de perfiles directas, los perfiles basados en activadores solo entregan resultados a un objeto de escucha global registrado con registerForAllProfilingResults. En tu devolución de llamada, llama a ProfilingResult.getTriggerType para identificar qué activador inició la captura y llama a ProfilingResult.getResultFilePath para ubicar el archivo de perfil guardado. Para obtener detalles sobre los nombres de los archivos de perfiles de activadores, la extracción de archivos con adb y la carga de resultados en segundo plano, consulta Cómo recuperar y subir perfiles.

Aunque los activadores persisten tras los reinicios de la app y del sistema, la devolución de llamada de resultado no lo hace. Registra tu devolución de llamada en un Executor en segundo plano después de que se complete el inicio de la app (para no ralentizar el inicio en frío) para recibir perfiles en las siguientes situaciones:

  • Mientras se ejecuta la app: En el caso de los activadores que no detienen ni bloquean la app, el sistema invoca la devolución de llamada en el Executor proporcionado cuando se produce el activador y se guarda el perfil.
  • Después de que finaliza la app: En el caso de los activadores que suelen implicar o preceder la finalización de la app (como TRIGGER_TYPE_ANR, TRIGGER_TYPE_OOM o TRIGGER_TYPE_KILL_RECENTS), o si la app se detiene antes de que finalice la captura, el sistema guarda el perfil en segundo plano y pone en cola el resultado en los reinicios de la app y del sistema. El sistema retiene los resultados en cola no entregados durante un máximo de 7 días y los entrega la próxima vez que se ejecuta tu app y llama a registerForAllProfilingResults.

Cómo funciona el seguimiento en segundo plano

Para equilibrar la sobrecarga de generación de perfiles con la captura de artefactos útiles, el sistema no ejecuta seguimientos en segundo plano de forma continua. En cambio, toma muestras de forma periódica iniciando seguimientos en segundo plano a intervalos aleatorios.

Los seguimientos en segundo plano activados por el sistema usan un búfer circular con un tamaño máximo definido por el sistema. Cuando se llena el búfer, los datos de seguimiento nuevos reemplazan los datos más antiguos. Como se muestra en la figura 1, cuando se activa un activador registrado mientras hay un seguimiento en segundo plano activo y hay una cuota de limitador de frecuencia disponible, el sistema guarda la instantánea del búfer en anillo que contiene la actividad más reciente que llevó al activador.

Diagrama que muestra cómo funcionan las instantáneas de seguimiento en segundo plano, con un búfer circular que captura datos antes de un evento de activación.
Figura 1: Cómo funcionan las instantáneas de seguimiento en segundo plano

Establece un tiempo de espera específico para el activador

Los activadores de alta frecuencia pueden consumir rápidamente la cuota del limitador de frecuencia de tu app. Para evitar que un solo tipo de activador agote tu cuota, usa setRateLimitingPeriodHours para agregar un período de inactividad mínimo por activador además del limitador de frecuencia del sistema.

Depura activadores de forma local

Debido a que los seguimientos en segundo plano se ejecutan en intervalos aleatorios en producción, para probar los activadores de forma local, se debe forzar un seguimiento en segundo plano continuo y omitir el limitador de frecuencia a nivel del sistema para tu paquete (se sigue aplicando cualquier tiempo de espera por activador establecido con setRateLimitingPeriodHours, y rate_limiter.disabled solo se aplica a las llamadas requestProfiling controladas por la app).

Para conocer los comandos de adb que permiten forzar el seguimiento en segundo plano, simular activadores desde la línea de comandos y, además, inspeccionar el estado del servicio, consulta Forzar la habilitación de seguimientos en segundo plano para probar activadores en Comandos de depuración para la generación de perfiles locales.