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_STARTyTRIGGER_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:
TRIGGER_TYPE_ANR(nivel de API 36)TRIGGER_TYPE_APP_FULLY_DRAWN(nivel de API 36)TRIGGER_TYPE_APP_REQUEST_RUNNING_TRACE(nivel de API 36.1)TRIGGER_TYPE_KILL_FORCE_STOP(nivel de API 36.1)TRIGGER_TYPE_KILL_RECENTS(nivel de API 36.1)TRIGGER_TYPE_KILL_TASK_MANAGER(nivel de API 36.1)TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE(nivel de API 37)
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:
TRIGGER_TYPE_COLD_START(nivel de API 37)TRIGGER_TYPE_OOM(nivel de API 37)
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:
TRIGGER_TYPE_ANOMALY(nivel de API 37)TRIGGER_TYPE_APP_COMPAT(nivel de API 37)
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:
- Obtén el administrador: Recupera el servicio
ProfilingManager. - Define un activador: Compila un
ProfilingTriggerparaTRIGGER_TYPE_APP_FULLY_DRAWN. Este evento ocurre cuando la app llama areportFullyDrawn()durante un inicio en frío iniciado por la actividad para informar que finalizó el inicio y es interactiva. - 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. - Registra el objeto de escucha: Llama a
registerForAllProfilingResultspara 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 degetResultFilePath(). - Agrega activadores: Registra la lista de activadores con
ProfilingManagerusandoaddProfilingTriggers. - Evento de activación: Publica una devolución de llamada de fotograma
Choreographerque llama areportFullyDrawn()una vez que se dibuja el siguiente fotograma, lo que emite el eventoTRIGGER_TYPE_APP_FULLY_DRAWNal 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 areportFullyDrawn().
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
Executorproporcionado 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_OOMoTRIGGER_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 aregisterForAllProfilingResults.
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.
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.