Android 11 (niveau d'API 30) ou version ultérieure est compatible avec le congélateur d'applications mises en cache. Cette fonctionnalité arrête l'exécution des processus mis en cache et réduit l'utilisation des ressources par les applications au comportement anormal qui pourraient tenter de fonctionner en mode cache.
Le congélateur d'applications mises en cache conserve les applications dans la RAM tout en les gardant hors du processeur. Si Android détermine qu'une application ne doit pas fonctionner, mais qu'elle peut être nécessaire à l'avenir, il gèle le processus de l'application au lieu de l'arrêter. Cela évite un démarrage à froid lorsque l'application est à nouveau nécessaire.
Android gèle les applications mises en cache en migrant leurs processus vers un cgroup gelé. Cela réduit la consommation du processeur actif et inactif en présence d'applications mises en cache actives. Vous pouvez activer le congélateur d'applications à l'aide d'un indicateur de configuration système ou d'une option pour les développeurs.
Dans Android 14 (niveau d'API 34) et versions ultérieures, le congélateur d'applications mises en cache inclut les comportements robustes suivants :
- Les processus d'application à l'état mis en cache sont figés 10 secondes après leur entrée dans cet état.
- Le système dégèle immédiatement un processus d'application figé lors d'un événement de cycle de vie. Ces événements incluent la réception d'un intent, le démarrage d'un service de job ou la reprise d'une activité par l'utilisateur.
ActivityManagerService gère tous les processus d'application et prend des décisions concernant le cycle de vie des applications. CachedAppOptimizer est chargé de figer le processus de l'application.
Lorsqu'un processus d'application est figé, tous ses threads sont suspendus et ne peuvent pas effectuer de travail du processeur tant qu'ils ne sont pas dégelés. Par conséquent, l'application ne peut pas effectuer de récupération de mémoire (GC) ni répondre aux événements de réduction de mémoire. Pour en savoir plus, consultez ComponentCallbacks2.onTrimMemory(int). Pour ce faire, à partir d'Android 14 :
- Les applications avec une instance
Activityvisible sont averties deTRIM_MEMORY_UI_HIDDENdès qu'elles passent en arrière-plan. Les applications qui restent dans un cycle de vie sans UI, comme les applications avec un service de premier plan, peuvent recevoirTRIM_MEMORY_BACKGROUND. Les autres événements de suppression ne sont pas transmis, car lorsque les applications sont éligibles à ces événements, elles sont censées être figées. - Peu de temps après le passage à l'état mis en cache, le système peut demander au runtime de l'application d'effectuer un GC en vue d'un éventuel gel.
- Lorsqu'un processus d'application est figé, des étapes de compaction de mémoire supplémentaires peuvent se produire, comme l'écriture de pages modifiées dans le stockage de sauvegarde et l'échange de pages anonymes vers la ZRAM.
- Si tous les processus d'une application spécifique sont figés, le système met fin à tous les sockets TCP actifs gérés par l'application. Cela empêche le côté serveur du socket d'envoyer des pings TCP keepalive qui réveilleraient le modem de l'appareil.
Les processus d'application mis en cache sont dégelés lorsque leur état de processus passe de l'état mis en cache à un état d'importance supérieure. Pour réduire les événements de dégel dans Android 14 et versions ultérieures, le système met en file d'attente les diffusions enregistrées en contexte lorsque l'application est à l'état mis en cache. Les diffusions enregistrées dans le contexte sont des récepteurs qu'une application enregistre de manière dynamique en appelant Context.registerReceiver. Le système ne distribue ces diffusions en file d'attente qu'une fois l'application dégelée. En revanche, le système ne met pas en file d'attente les diffusions déclarées par le fichier manifeste.
Les diffusions déclarées par le fichier manifeste sont des récepteurs déclarés de manière statique dans AndroidManifest.xml à l'aide de l'élément <receiver>. Le système dégèle immédiatement l'application mise en cache pour diffuser les diffusions déclarées par le fichier manifeste.
Impact sur l'état du système
Android met fin au processus d'application mis en cache le moins récemment utilisé s'il existe plus de MAX_CACHED_PROCESSES processus d'application mis en cache. Sur les appareils compatibles équipés d'Android 14 ou version ultérieure, MAX_CACHED_PROCESSES est considérablement augmenté, ce qui permet aux appareils de conserver beaucoup plus de processus d'application mis en cache dans la RAM.
Le fait de conserver davantage d'applications en cache dans la RAM permet de réduire les démarrages à froid jusqu'à 30 %, les réductions étant proportionnelles à la RAM totale de l'appareil. Dans le même temps, la consommation du processeur par les applications mises en cache est minimisée, ce qui permet de réaliser d'importantes économies de batterie.
Exemptions pour les congélateurs
Dans certaines conditions, un processus d'application peut passer à l'état mis en cache, mais rester non figé. Ces exemptions sont des détails d'implémentation et peuvent changer dans les futures versions d'Android :
- Verrouillage de fichiers : si un processus mis en cache détient un verrouillage de fichier qui bloque d'autres processus non mis en cache, le processus qui détient le verrouillage n'est pas figé.
BIND_WAIVE_PRIORITYbindings : les processus d'application avec des liaisons entrantes créées à l'aide deContext.BIND_WAIVE_PRIORITYpeuvent passer à l'état mis en cache, mais rester non figés jusqu'à ce que tous les processus client connectés soient également mis en cache. Cette exemption s'applique aux applications multiprocessus, telles que les navigateurs Web utilisant des onglets personnalisés.
Implémenter le congélateur d'applications
Le congélateur d'applications mises en cache utilise le congélateur cgroup v2 du noyau. Les appareils livrés avec un noyau compatible peuvent l'activer. Activez l'option pour les développeurs Suspend execution for cached apps (Suspendre l'exécution pour les applications mises en cache) ou définissez l'indicateur de configuration de l'appareil activity_manager_native_boot use_freezer sur true. Exemple :
adb shell device_config put activity_manager_native_boot use_freezer true && adb rebootLe congélateur est désactivé lorsque vous définissez l'indicateur use_freezer sur false ou que vous désactivez l'option pour les développeurs. Exemple :
adb shell device_config put activity_manager_native_boot use_freezer false && adb rebootVous pouvez activer ou désactiver ce paramètre en modifiant la configuration d'un appareil dans une version ou une mise à jour logicielle.
Pour remplacer MAX_CACHED_PROCESSES, par exemple pour définir la valeur sur 1024 pour les tests :
adb shell device_config put activity_manager max_cached_processes 1024adb shell device_config set_sync_disabled_for_tests persistent
Pour annuler le forçage de MAX_CACHED_PROCESSES :
adb shell device_config delete activity_manager max_cached_processesadb shell device_config set_sync_disabled_for_tests none
À partir d'Android 16 (niveau d'API 36) et versions ultérieures, Android fournit des API publiques officielles, telles que IBinder.FrozenStateChangeCallback et IBinder.addFrozenStateChangeCallback, pour observer quand les processus à distance sont figés ou non. Les composants qui interagissent avec des applications susceptibles d'être mises en cache peuvent utiliser ces API pour suivre l'état figé des processus à distance.
Configuration requise pour l'appareil et le noyau
Le congélateur d'applications mises en cache nécessite la prise en charge du cgroup v2 du noyau. De plus, les notifications de changement d'état de gel à l'aide de IBinder.FrozenStateChangeCallback nécessitent la prise en charge du pilote Binder du noyau, qui est standard dans les noyaux communs Android (ACK) et les images de noyau génériques (GKI) à partir d'Android 14 (niveau d'API 34) et versions ultérieures.
Vous pouvez vérifier si un appareil est compatible avec ces fonctionnalités à l'aide des commandes adb standards, comme suit :
Vérifiez si le congélateur est activé sur l'appareil (pour les versions utilisateur ou de débogage) :
adb shell device_config get activity_manager_native_boot use_freezerVous pouvez également vérifier que les processus sont activement figés par le système :
adb shell dumpsys activity | grep -A 20 "Apps frozen:"Vérifier la compatibilité du contrôleur de congélation cgroup v2 (pour n'importe quel appareil) :
Vérifiez que
freezerfigure parmi les contrôleurs cgroup v2 disponibles :adb shell cat /sys/fs/cgroup/cgroup.controllersSur un appareil rooté ou une version userdebug, vérifiez que le nœud freezer cgroup v2 est monté dans un cgroup enfant :
adb root && adb shell ls /sys/fs/cgroup/uid_0/cgroup.freezeSi ce fichier existe, le noyau est compatible avec le freezer cgroup v2.
Vérifiez la compatibilité des notifications de changement d'état de blocage (pour n'importe quel appareil) :
Sur les appareils équipés d'Android 14 ou version ultérieure avec des pilotes de binder Generic Kernel Image (GKI) compatibles,
IBinder.addFrozenStateChangeCallbackenregistre les rappels avec succès. Si le pilote Binder du noyau sous-jacent n'est pas compatible avec les notifications de gel, la méthode génèreUnsupportedOperationException.
Gérer les fonctionnalités personnalisées
Les processus d'application ne sont pas censés effectuer de tâches lorsqu'ils sont mis en cache, mais certaines applications peuvent avoir des fonctionnalités personnalisées prises en charge par des processus qui sont censés s'exécuter lorsqu'ils sont mis en cache. Lorsque le congélateur d'applications est activé sur un appareil exécutant de telles applications, les processus mis en cache sont figés et peuvent empêcher le fonctionnement des fonctionnalités personnalisées.
Pour contourner ce problème, vous pouvez définir l'état du processus sur "non mis en cache" avant que le processus n'ait besoin d'effectuer une tâche. Cette modification permet aux applications de rester actives. Par exemple, un service de premier plan lié ou un état de premier plan sont des exemples d'états actifs.
Modes de défaillance courants
Lorsque les processus d'application sont figés, une communication inter-processus (IPC) ou une planification des tâches inappropriées peuvent entraîner l'arrêt de l'application ou un comportement inattendu.
Transactions de liaisons synchrones vers des processus figés
Lorsqu'un processus d'application cliente envoie une transaction Binder synchrone à un processus d'application serveur figé, le système met immédiatement fin au processus d'application serveur. Cela empêche le thread client de se bloquer indéfiniment en attendant une réponse du serveur figé. Le thread client reçoit ensuite RemoteException, et tous les écouteurs enregistrés sont déclenchés. Pour en savoir plus, consultez la section IBinder.linkToDeath.
Cause première : cette défaillance est généralement due à un bug dans l'application cliente. Lorsqu'un client se lie à un service, le processus serveur est lié au client et ne peut pas passer à l'état mis en cache avant le client. Pour en savoir plus, consultez la section Context.bindService. Toutefois, une fois que le client appelle Context.unbindService, le processus du serveur peut être mis en cache et figé. Si le client continue d'utiliser la référence IBinder mise en cache après la dissociation, il risque de communiquer avec un processus figé.
Pour éviter ce problème, assurez-vous que les applications clientes suppriment les références IBinder immédiatement après avoir appelé Context.unbindService.
Gérer les rappels à distance aux processus client
Les services et les composants système qui conservent des rappels de binder de longue durée pour les processus client peuvent éviter les échecs synchrones et les dépassements de mémoire tampon asynchrones en suivant l'état figé du client :
- Enregistrer un écouteur de changement d'état : utilisez
IBinder.addFrozenStateChangeCallbacksur les jetons de binder client entrants pour recevoir des notifications lorsque le processus client entre dans le congélateur ou en sort. - Mettre en pause l'envoi pendant la période de gel : lorsqu'un client entre dans
STATE_FROZEN, mettez en pause l'envoi des rappels ou des mises à jour d'état à ce client. - Reprendre et distribuer après la réactivation : lorsque le client passe à l'état
STATE_UNFROZEN, reprenez la distribution des rappels et distribuez les mises à jour groupées ou consolidées nécessaires. - Utiliser RemoteCallbackList : les services système utilisant
RemoteCallbackListpeuvent configurer des règles d'appelant figées pour mettre en pause et reprendre automatiquement l'envoi de rappel sans avoir à gérer une logique de suivi manuel. Pour en savoir plus, consultez Recommandations concernant le bloqueur de Binder pour les services système.
Dépassement de mémoire tampon de transaction de binder asynchrone
Lorsqu'un processus d'application serveur reçoit des transactions de liaison asynchrones (oneway) alors qu'il est figé, les transactions sont mises en mémoire tampon dans une mémoire tampon par processus. Si le serveur reçoit trop de transactions asynchrones pendant qu'il est figé, le tampon déborde et le système met fin au processus de l'application serveur.
Pour éviter ce dépassement de mémoire tampon, évitez d'envoyer un nombre excessif de transactions Binder asynchrones à des processus susceptibles d'être mis en cache ou figés.
Exécution répétée des tâches planifiées lors du dégel
Si une application exécute des tâches répétitives, elles sont suspendues pendant que le processus est figé. Pour en savoir plus, consultez ScheduledThreadPoolExecutor.scheduleAtFixedRate ou Timer.scheduleAtFixedRate. Lorsque le processus se débloque, les exécutions manquées accumulées peuvent s'exécuter rapidement, les unes après les autres, pratiquement sans délai.
Pour éviter une vague d'exécutions lorsque l'application se débloque, utilisez scheduleWithFixedDelay au lieu de scheduleAtFixedRate pour les tâches en arrière-plan. Vous pouvez également utiliser WorkManager.
Tester et résoudre les problèmes liés au congélateur d'applications
Pour vérifier que le congélateur d'applications fonctionne comme prévu ou pour résoudre les problèmes liés au congélateur, utilisez les outils et commandes de diagnostic suivants :
Commandes du gestionnaire d'activités
Vous pouvez utiliser les commandes adb shell am pour contrôler manuellement le gel et la compression d'un processus spécifique :
Forcer un processus à se figer :
adb shell am freeze <process>Forcer un processus à se débloquer :
adb shell am unfreeze <process>Forcez une compaction complète de la mémoire sur un processus :
adb shell am compact full <process>
Examen de Logcat
Consultez logcat pour afficher les entrées figées et dégelées chaque fois qu'un processus migre vers ou hors du congélateur :
adb logcat | grep -i "\(freezing\|froze\)"Les journaux de motifs de dégel affichent les valeurs énumérées du protocole UnfreezeReason.
Inspection Dumpsys
Recherchez une liste des processus figés à l'aide de dumpsys activity :
adb shell dumpsys activity | grep -A 20 "Apps frozen:"Vérifiez la présence du fichier /sys/fs/cgroup/uid_0/cgroup.freeze.
ApplicationExitInfo
Pour interroger le motif de l'arrêt d'un processus précédent, consultez ActivityManager.getHistoricalProcessExitReasons.
Si un processus d'application a été arrêté en raison d'un problème lié au congélateur, par exemple la réception d'une transaction de binder synchrone pendant la congélation, le motif de sortie est défini sur ApplicationExitInfo.REASON_FREEZER.
Traçage Perfetto
Les événements liés au congélateur sont émis sur une piste nommée Freezer sous le processus system_server dans les traces Perfetto :
- Les tranches
FreezeetUnfreezeindiquent quand un processus change d'état. - Les événements
updateAppFreezeStateLSPindiquent quand le serveur système réexamine les attributs du processus pour décider de le geler ou de le dégeler.
Vous pouvez inspecter ces événements directement dans l'UI Perfetto ou les analyser à l'aide de 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 %");
Dans la bibliothèque standard PerfettoSQL, les événements de blocage sont également récapitulés dans le tableau android_freezer_events.