Android 11 (nivel de API 30) y versiones posteriores admiten el congelador de apps en caché. Esta función detiene la ejecución de procesos almacenados en caché y reduce el uso de recursos por parte de apps con un comportamiento inadecuado que podrían intentar operar mientras están almacenadas en caché.
El congelador de apps en caché mantiene las apps en la RAM, pero no en la CPU. Si Android determina que una app no debería realizar trabajo, pero podría ser necesaria en el futuro, congela el proceso de la app en lugar de finalizarlo. Esto evita un inicio en frío cuando se vuelve a necesitar la app.
Android congela las apps almacenadas en caché migrando sus procesos a un cgroup congelado. Esto reduce el consumo de CPU activa y en inactividad en presencia de apps activas en caché. Puedes habilitar el congelador de apps con una marca de configuración del sistema o una opción para desarrolladores.
En Android 14 (nivel de API 34) y versiones posteriores, el congelador de apps en caché incluye los siguientes comportamientos sólidos:
- Los procesos de la app en estado almacenado en caché se congelan 10 segundos después de entrar en ese estado.
- El sistema descongela de inmediato un proceso de app congelado durante un evento del ciclo de vida. Estos eventos incluyen la recepción de un intent, el inicio de un servicio de trabajo o la reanudación de una actividad por parte del usuario.
ActivityManagerService administra todos los procesos de la app y toma decisiones sobre el ciclo de vida de la app. CachedAppOptimizer es responsable de congelar el proceso de la app.
Cuando se inmoviliza un proceso de la app, se suspenden todos sus subprocesos y no pueden realizar trabajo de CPU hasta que se descongelen. Como resultado, la app no puede realizar la recolección de basura (GC) ni responder a los eventos de reducción de memoria. Para obtener más información, consulta ComponentCallbacks2.onTrimMemory(int). Para adaptarse a este cambio, a partir de Android 14, se implementaron las siguientes medidas:
- Las apps con una instancia de
Activityvisible reciben una notificación deTRIM_MEMORY_UI_HIDDENen cuanto pasan a segundo plano. Las apps que permanecen en un ciclo de vida sin una IU, como las apps con un servicio en primer plano, pueden recibirTRIM_MEMORY_BACKGROUND. No se entregan otros eventos de recorte, ya que, cuando las apps son aptas para esos eventos, se espera que estén congeladas. - Poco después de entrar en el estado almacenado en caché, el sistema podría solicitar al tiempo de ejecución de la app que realice una GC en preparación para una posible congelación.
- Cuando se congela un proceso de la app, es posible que se produzcan pasos adicionales de compactación de memoria, como escribir páginas no sincronizadas en el almacenamiento de respaldo y cambiar páginas anónimas a ZRAM.
- Si todos los procesos de una app en particular están congelados, el sistema finaliza todos los sockets TCP activos que mantiene la app. Esto evita que el servidor del socket envíe pings de TCP keepalive que activarían el módem del dispositivo.
Los procesos de la app almacenados en caché se descongelan cuando su estado de proceso se eleva de almacenado en caché a un estado de mayor importancia. Para reducir los eventos de descongelamiento en Android 14 y versiones posteriores, el sistema pone en cola las transmisiones registradas en el contexto mientras la app está en el estado almacenado en caché. Las transmisiones registradas en el contexto son receptores que una app registra de forma dinámica llamando a Context.registerReceiver. El sistema entrega estas transmisiones en cola solo después de que se descongele la app. En cambio, el sistema no pone en cola las transmisiones declaradas en el manifiesto.
Las transmisiones declaradas en el manifiesto son receptores declarados de forma estática en AndroidManifest.xml con el elemento <receiver>. El sistema descongela de inmediato la app almacenada en caché para entregar las transmisiones declaradas en el manifiesto.
Impacto en el estado del sistema
Android finaliza el proceso de la app almacenada en caché que se usó menos recientemente si hay más de MAX_CACHED_PROCESSES procesos de apps almacenados en caché. En los dispositivos compatibles que ejecutan Android 14 o versiones posteriores, MAX_CACHED_PROCESSES se incrementa de manera significativa, lo que permite que los dispositivos mantengan muchos más procesos de apps almacenados en caché en la RAM.
Mantener más apps almacenadas en caché en la RAM genera una reducción de hasta el 30% en los inicios en frío, y las reducciones se ajustan según la RAM total del dispositivo. Al mismo tiempo, se minimiza el consumo de CPU de las apps almacenadas en caché, lo que genera ahorros significativos de batería.
Exenciones de congeladores
En ciertas condiciones, un proceso de la app puede entrar en el estado almacenado en caché, pero permanecer sin congelar. Estas exenciones son detalles de implementación y podrían cambiar en futuras versiones de Android:
- Bloqueos de archivos: Si un proceso almacenado en caché mantiene un bloqueo de archivos que bloquea otros procesos no almacenados en caché, el proceso que mantiene el bloqueo no se congela.
- Vinculaciones de
BIND_WAIVE_PRIORITY: Los procesos de la app con vinculaciones entrantes creadas conContext.BIND_WAIVE_PRIORITYpueden ingresar al estado almacenado en caché, pero permanecen sin congelar hasta que todos los procesos de cliente conectados también se almacenan en caché. Esta exención admite apps de varios procesos, como los navegadores web que usan pestañas personalizadas.
Implementa el congelador de apps
El congelador de apps almacenadas en caché aprovecha el congelador cgroup v2 del kernel. Los dispositivos que se envían con un kernel compatible pueden habilitarlo. Habilita la opción para desarrolladores Suspender la ejecución de apps en caché o establece el parámetro de configuración del dispositivo activity_manager_native_boot use_freezer en true. Por ejemplo:
adb shell device_config put activity_manager_native_boot use_freezer true && adb rebootEl congelador se inhabilita cuando configuras la marca use_freezer en false o inhabilitas la opción para desarrolladores. Por ejemplo:
adb shell device_config put activity_manager_native_boot use_freezer false && adb rebootPuedes activar o desactivar este parámetro de configuración cambiando la configuración del dispositivo en una actualización o versión de software.
Para anular MAX_CACHED_PROCESSES, por ejemplo, para establecer el valor en 1,024 para realizar pruebas, haz lo siguiente:
adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistentPara revertir la anulación de MAX_CACHED_PROCESSES, sigue estos pasos:
adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests noneEl congelador de apps no expone APIs oficiales ni tiene un cliente de implementación de referencia, pero usa las APIs ocultas del sistema setProcessFrozen para congelar un proceso individual y enableFreezer para habilitar o inhabilitar la congelación de forma global.
Cómo controlar funciones personalizadas
No se espera que los procesos de la app realicen ningún trabajo cuando se almacenan en caché, pero algunas apps pueden tener funciones personalizadas compatibles con procesos que se espera que se ejecuten mientras se almacenan en caché. Cuando el congelador de apps está habilitado en un dispositivo que ejecuta esas apps, los procesos almacenados en caché se congelan y pueden impedir que funcionen las funciones personalizadas.
Como solución alternativa, puedes cambiar el estado del proceso a no almacenado en caché antes de que el proceso necesite realizar cualquier trabajo. Este cambio permite que las apps permanezcan activas. Entre los ejemplos de estados activos, se incluyen un servicio en primer plano vinculado o un estado en primer plano.
Modos de falla comunes
Cuando los procesos de la app se congelan, la comunicación entre procesos (IPC) o la programación de tareas inadecuadas pueden provocar el cierre de la app o un comportamiento inesperado.
Transacciones de Binder síncronas a procesos congelados
Cuando un proceso de una app cliente envía una transacción de Binder síncrona a un proceso de una app servidor que está inactivo, el sistema finaliza de inmediato el proceso de la app servidor. Esto evita que el subproceso del cliente se bloquee de forma indefinida mientras espera una respuesta del servidor inactivo. Luego, el subproceso del cliente recibe RemoteException y se activan los objetos de escucha registrados. Para obtener más información, consulta IBinder.linkToDeath.
Causa raíz: Por lo general, este error se debe a un bug en la app cliente. Cuando un cliente se vincula a un servicio, el proceso del servidor se vincula al cliente y no puede ingresar al estado almacenado en caché antes de que lo haga el cliente. Para obtener más información, consulta Context.bindService. Sin embargo, una vez que el cliente llama a Context.unbindService, el proceso del servidor puede almacenarse en caché y congelarse. Si el cliente sigue usando la referencia IBinder almacenada en caché después de desvincularse, corre el riesgo de comunicarse con un proceso inactivo.
Para evitar este problema, asegúrate de que las apps cliente descarten las referencias de IBinder inmediatamente después de llamar a Context.unbindService.
Desbordamiento del búfer de transacciones de Binder asíncronas
Cuando un proceso de la app del servidor recibe transacciones de Binder asíncronas (oneway) mientras está inactivo, las transacciones se almacenan en un búfer por proceso. Si el servidor recibe demasiadas transacciones asíncronas mientras está inactivo, se produce un desbordamiento del búfer y el sistema finaliza el proceso de la app del servidor.
Para evitar este desbordamiento del búfer, no envíes transacciones de Binder asíncronas excesivas a procesos que podrían estar almacenados en caché o congelados.
Ejecución repetida de tareas programadas después de la reactivación
Si una app ejecuta tareas repetitivas, estas se suspenden mientras el proceso está inactivo. Para obtener más información, consulta ScheduledThreadPoolExecutor.scheduleAtFixedRate o Timer.scheduleAtFixedRate. Cuando el proceso se desbloquea, las ejecuciones perdidas acumuladas pueden ejecutarse rápidamente de forma consecutiva sin prácticamente ninguna demora.
Para evitar una oleada de ejecuciones cuando la app se descongele, usa scheduleWithFixedDelay en lugar de scheduleAtFixedRate para las tareas en segundo plano. También puedes usar WorkManager.
Prueba y soluciona problemas del congelador de apps
Para verificar que el congelador de apps funcione según lo previsto o solucionar problemas relacionados con el congelador, usa las siguientes herramientas y comandos de diagnóstico:
Comandos del administrador de actividades
Puedes usar los comandos adb shell am para controlar manualmente la inmovilización y la compactación de un proceso específico:
Cómo forzar la inmovilización de un proceso:
adb shell am freeze <process>Cómo forzar el desbloqueo de un proceso:
adb shell am unfreeze <process>Forzar una compactación de memoria completa en un proceso:
adb shell am compact full <process>
Examen de Logcat
Consulta el logcat para ver las entradas congeladas y descongeladas cada vez que un proceso migra dentro o fuera del congelador:
adb logcat | grep -i "\(freezing\|froze\)"Los registros de motivos de descongelación generan valores enumerados del enum del búfer de protocolo UnfreezeReason.
Inspección de Dumpsys
Verifica la lista de procesos congelados con dumpsys activity:
adb shell dumpsys activity | grep -A 20 "Apps frozen:"Verifica la presencia del archivo /sys/fs/cgroup/uid_0/cgroup.freeze.
ApplicationExitInfo
Para consultar el motivo del cierre de un proceso anterior, consulta ActivityManager.getHistoricalProcessExitReasons.
Si se finalizó un proceso de la app debido a un problema relacionado con el congelador, como recibir una transacción de Binder síncrona mientras está congelado, el motivo de salida se establece en ApplicationExitInfo.REASON_FREEZER.
Registro de Perfetto
Los eventos relacionados con el congelador se emiten a un registro llamado Freezer en el proceso system_server en los registros de Perfetto:
- Los segmentos
FreezeyUnfreezeindican cuándo cambia el estado de un proceso. - Los eventos
updateAppFreezeStateLSPmuestran cuándo el servidor del sistema vuelve a examinar los atributos del proceso para tomar decisiones sobre la congelación o descongelación.
Puedes inspeccionar estos eventos directamente en la IU de Perfetto o analizarlos con PerfettoSQL:
INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");
En la biblioteca estándar de PerfettoSQL, los eventos de congelación también se resumen en la tabla android_freezer_events.