Gérer les modifications de configuration

Certaines configurations d'appareil peuvent changer pendant l'exécution de l'application. En voici quelques exemples:

  • Taille d'affichage de l'application
  • Orientation de l'écran
  • Taille et épaisseur de police
  • Langue
  • Mode sombre et mode clair
  • Disponibilité du clavier

La plupart de ces modifications de configuration sont dues à une interaction de l'utilisateur. Par exemple, la rotation ou le pliage de l'appareil modifie la quantité d'espace à l'écran disponible pour votre application. De même, la modification des paramètres de l'appareil tels que la taille de la police, la langue ou le thème préféré modifie leurs valeurs respectives dans l'objet Configuration.

Ces paramètres nécessitent généralement des modifications suffisamment importantes de l'interface utilisateur de votre application pour que la plate-forme Android dispose d'un mécanisme spécifique en cas de modification. Ce mécanisme est la recréation d'Activity.

Recréation d'activité

Le système recrée un Activity lorsqu'un changement de configuration se produit. Pour ce faire, le système appelle onDestroy() et détruit l'instance Activity existante. Il crée ensuite une instance à l'aide de onCreate(), et cette nouvelle instance Activity est initialisée avec la nouvelle configuration mise à jour. Cela signifie également que le système recrée également l'interface utilisateur avec la nouvelle configuration.

Le comportement de recréation aide votre application à s'adapter à de nouvelles configurations en l'actualise automatiquement avec d'autres ressources correspondant à la nouvelle configuration de l'appareil.

Exemple pour les loisirs

Prenons l'exemple d'un TextView qui affiche un titre statique à l'aide de android:text="@string/title", tel que défini dans un fichier XML de mise en page. Lorsque la vue est créée, elle définit le texte exactement une fois, en fonction de la langue actuelle. Si la langue change, le système recrée l'activité. Par conséquent, le système recrée également la vue et l'initialise avec la valeur correcte en fonction de la nouvelle langue.

La recréation efface également tout état conservé en tant que champs dans Activity ou dans l'un des objets Fragment, View ou d'autres objets qu'il contient. En effet, la recréation d'Activity crée une toute nouvelle instance de Activity et de l'UI. De plus, l'ancien Activity n'est plus visible ni valide. Par conséquent, toutes les références restantes à cet élément ou aux objets qu'il contient sont obsolètes. Ils peuvent entraîner des bugs, des fuites de mémoire et des plantages.

Attentes des utilisateurs

L'utilisateur d'une application s'attend à ce que l'état soit conservé. Si un utilisateur remplit un formulaire et ouvre une autre application en mode multifenêtre pour référencer des informations, l'expérience utilisateur peut nuire à l'expérience s'il revient à un formulaire effacé ou à un autre endroit de l'application. En tant que développeur, vous devez fournir une expérience utilisateur cohérente en modifiant la configuration et en recréant l'activité.

Pour vérifier si l'état est préservé dans votre application, vous pouvez effectuer des actions qui entraînent des modifications de configuration lorsque l'application est au premier plan et en arrière-plan. Ces actions incluent :

  • Faire pivoter l'appareil
  • Activation du mode multifenêtre
  • Redimensionner l'application en mode multifenêtre ou dans une fenêtre de format libre
  • Plier un appareil pliable avec plusieurs écrans
  • Modifier le thème du système (mode sombre ou mode clair, par exemple)
  • Modifier la taille de la police
  • Modification de la langue du système ou de l'application
  • Connecter ou déconnecter un clavier physique
  • Connecter ou déconnecter une station d'accueil

Il existe trois approches principales pour conserver l'état pertinent via la recréation d'Activity. Les options à utiliser dépendent du type d'état que vous souhaitez conserver:

  • Persistance locale pour gérer l'arrêt du processus pour les données complexes ou volumineuses. Le stockage local persistant inclut les bases de données ou DataStore.
  • Les objets conservés tels que les instances ViewModel pour gérer l'état lié à l'interface utilisateur en mémoire lorsque l'utilisateur utilise activement l'application.
  • État d'instance enregistré pour gérer l'arrêt de processus déclenché par le système et conserver l'état temporaire qui dépend de l'entrée utilisateur ou de la navigation.

Pour en savoir plus sur les API associées à chacun de ces éléments et sur leur utilisation appropriée, consultez la section Enregistrer les états de l'interface utilisateur.

Limiter la recréation d'activité

Vous pouvez empêcher la recréation automatique de l'activité pour certaines modifications de configuration. La recréation d'une Activity entraîne la recréation de l'ensemble de l'interface utilisateur et de tous les objets dérivés de Activity. Vous avez peut-être de bonnes raisons d'éviter cela. Par exemple, il se peut que votre application n'ait pas besoin de mettre à jour les ressources lors d'une modification de configuration spécifique, ou que vos performances soient limitées. Dans ce cas, vous pouvez déclarer que votre activité gère elle-même la modification de configuration et empêche le système de redémarrer votre activité.

Pour désactiver la recréation d'activité pour des modifications de configuration particulières, ajoutez le type de configuration à android:configChanges dans l'entrée <activity> du fichier AndroidManifest.xml. Les valeurs possibles sont disponibles dans la documentation pour l'attribut android:configChanges.

Le code de fichier manifeste suivant désactive la recréation de Activity pour MyActivity lorsque l'orientation de l'écran et la disponibilité du clavier changent:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

Certaines modifications de configuration entraînent toujours le redémarrage de l'activité. Vous ne pouvez pas les désactiver. Par exemple, vous ne pouvez pas désactiver le changement des couleurs dynamiques introduit dans Android 12L (niveau d'API 32).

Réagir aux changements de configuration dans le système de vues

Dans le système View, lorsqu'une modification de la configuration se produit pour laquelle vous avez désactivé la recréation de Activity, l'activité reçoit un appel à Activity.onConfigurationChanged(). Toutes les vues associées reçoivent également un appel à View.onConfigurationChanged(). Pour les modifications de configuration que vous n'avez pas ajoutées à android:configChanges, le système recrée l'activité comme d'habitude.

La méthode de rappel onConfigurationChanged() reçoit un objet Configuration qui spécifie la nouvelle configuration de l'appareil. Lisez les champs de l'objet Configuration pour déterminer votre nouvelle configuration. Pour effectuer les modifications suivantes, mettez à jour les ressources que vous utilisez dans votre interface. Lorsque le système appelle cette méthode, l'objet Resources de votre activité est mis à jour pour renvoyer des ressources en fonction de la nouvelle configuration. Cela vous permet de réinitialiser des éléments de votre interface utilisateur sans que le système ne redémarre votre activité.

Par exemple, l'implémentation de onConfigurationChanged() suivante vérifie si un clavier est disponible:

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
    } else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
    }
}

Java

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
    } else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
    }
}

Si vous n'avez pas besoin de mettre à jour votre application en fonction de ces modifications de configuration, vous ne pouvez pas implémenter onConfigurationChanged(). Dans ce cas, toutes les ressources utilisées avant la modification de la configuration sont toujours utilisées, et vous avez seulement évité le redémarrage de votre activité. Par exemple, une application TV peut ne pas vouloir réagir lorsqu'un clavier Bluetooth est connecté ou déconnecté.

Conserver l'état

Lorsque vous utilisez cette technique, vous devez toujours conserver l'état pendant le cycle de vie normal d'une activité. Cela est dû aux éléments suivants:

  • Modifications inévitables:les modifications de configuration que vous ne pouvez pas empêcher peuvent redémarrer votre application.
  • Arrêt de processus:votre application doit pouvoir gérer un arrêt de processus déclenché par le système. Si l'utilisateur quitte votre application et qu'elle passe en arrière-plan, le système peut la détruire.

Réagir aux modifications de configuration dans Jetpack Compose

Jetpack Compose permet à votre application de réagir plus facilement aux changements de configuration. Toutefois, si vous désactivez la recréation d'Activity pour toutes les modifications de configuration lorsque cela est possible, votre application doit tout de même gérer correctement les modifications de configuration.

L'objet Configuration est disponible dans la hiérarchie de l'interface utilisateur Compose, avec la composition LocalConfiguration en local. À chaque modification, les fonctions modulables qui lisent depuis LocalConfiguration.current se recomposent. Pour en savoir plus sur le fonctionnement de la composition locale, consultez la section Données à champ d'application local avec CompositionLocal.

Exemple

Dans l'exemple suivant, un composable affiche une date dans un format spécifique. Le composable réagit aux modifications de configuration des paramètres régionaux du système en appelant ConfigurationCompat.getLocales() avec LocalConfiguration.current.

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

Pour éviter la recréation d'Activity lorsque les paramètres régionaux changent, le Activity hébergeant le code Compose doit désactiver les modifications de configuration des paramètres régionaux. Pour ce faire, définissez android:configChanges sur locale|layoutDirection.

Modifications de configuration: concepts clés et bonnes pratiques

Voici les concepts clés à connaître lorsque vous apportez des modifications de configuration:

  • Configurations:les configurations d'appareil définissent la façon dont l'interface utilisateur s'affiche pour l'utilisateur, comme la taille d'affichage de l'application, les paramètres régionaux ou le thème système.
  • Modifications de la configuration:les configurations changent grâce à une interaction de l'utilisateur. Par exemple, l'utilisateur peut modifier les paramètres de l'appareil ou la manière dont il interagit physiquement avec l'appareil. Il n'existe aucun moyen d'empêcher les modifications de configuration.
  • Recréation de Activity:les modifications de configuration entraînent la recréation de Activity par défaut. Il s'agit d'un mécanisme intégré permettant de réinitialiser l'état de l'application pour la nouvelle configuration.
  • Destruction de Activity:la recréation d'Activity entraîne la destruction de l'ancienne instance Activity par le système et la création d'une nouvelle instance à sa place. L'ancienne instance est désormais obsolète. Toute référence restante à celui-ci entraîne des fuites de mémoire, des bugs ou des plantages.
  • État:l'état de l'ancienne instance Activity n'est pas présent dans la nouvelle instance Activity, car il s'agit de deux instances d'objet différentes. Conservez l'état de l'application et de l'utilisateur comme décrit dans Enregistrer les états de l'interface utilisateur.
  • Désactiver:désactiver la recréation d'activité en cas de modification de la configuration est une optimisation potentielle. Votre application doit être mise à jour correctement en réaction à la nouvelle configuration.

Pour offrir une expérience utilisateur de qualité, respectez les bonnes pratiques suivantes:

  • Préparez-vous à des changements de configuration fréquents:ne partez pas du principe que les modifications de configuration sont rares ou jamais se produire, quels que soient le niveau d'API, le facteur de forme ou le kit d'interface utilisateur. Lorsqu'un utilisateur modifie la configuration, il s'attend à ce que les applications se mettent à jour et continuent de fonctionner correctement avec la nouvelle configuration.
  • Préserver l'état:ne perdez pas l'état de l'utilisateur lors de la recréation d'Activity. Conservez l'état comme décrit dans Enregistrer les états de l'interface utilisateur.
  • Évitez de désactiver la recréation de Activity en tant que raccourci pour éviter la perte d'état. Pour désactiver la recréation d'activité, vous devez tenir la promesse de gérer la modification. Vous pouvez toujours perdre l'état en raison de la recréation de Activity à la suite d'autres modifications de configuration, de l'arrêt du processus ou de la fermeture de l'application. Il est impossible de désactiver complètement la recréation de Activity. Conservez l'état comme décrit dans Enregistrer les états de l'interface utilisateur.
  • N'évitez pas les changements de configuration:n'appliquez pas de restrictions sur l'orientation, le format ou le redimensionnement pour éviter les changements de configuration et la recréation d'Activity. Cela a un impact négatif sur les utilisateurs qui souhaitent utiliser votre application de la manière qui leur convient.

Gérer les modifications de configuration basées sur la taille

Les modifications de configuration basées sur la taille peuvent se produire à tout moment. Elles sont plus probables lorsque votre application s'exécute sur un appareil à grand écran où les utilisateurs peuvent passer en mode multifenêtre. Il s'attend à ce que votre application fonctionne correctement dans cet environnement.

Il existe deux types généraux de changements de taille: importants et non significatifs. Un changement de taille significatif se produit lorsqu'un ensemble différent d'autres ressources s'applique à la nouvelle configuration en raison d'une différence de taille d'écran, telle que la largeur, la hauteur ou la plus petite largeur. Ces ressources incluent celles que l'application se définit et celles de ses bibliothèques.

Limiter la recréation d'activité pour les modifications de configuration en fonction de la taille

Lorsque vous désactivez la recréation d'Activity pour les modifications de configuration basées sur la taille, le système ne recrée pas la Activity. À la place, il reçoit un appel vers Activity.onConfigurationChanged(). Toutes les vues associées reçoivent un appel à View.onConfigurationChanged().

La recréation d'Activity est désactivée pour les modifications de configuration basées sur la taille lorsque android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout figure dans votre fichier manifeste.

Autoriser la recréation d'activité pour les modifications de configuration en fonction de la taille

Sur Android 7.0 (niveau d'API 24) ou version ultérieure, la recréation d'Activity n'a lieu que pour les modifications de configuration en fonction de la taille si celles-ci sont importantes. Lorsque le système ne recrée pas un Activity en raison d'une taille insuffisante, il peut appeler Activity.onConfigurationChanged() et View.onConfigurationChanged() à la place.

Vous devez observer certaines mises en garde concernant les rappels Activity et View lorsque Activity n'est pas recréée:

  • Sur Android 11 (niveau d'API 30) à Android 13 (niveau d'API 33), Activity.onConfigurationChanged() n'est pas appelé.
  • Dans certains cas, il est possible que View.onConfigurationChanged() ne soit pas appelé sur Android 12L (niveau d'API 32) ni sur les premières versions d'Android 13 (niveau d'API 33). Il s'agit d'un problème connu. Pour en savoir plus, consultez ce problème public. Ce problème a depuis été résolu dans les versions ultérieures d'Android 13 et d'Android 14.

Pour le code qui dépend de l'écoute des modifications de configuration basées sur la taille, nous vous recommandons d'utiliser un utilitaire View avec un View.onConfigurationChanged() remplacé au lieu de compter sur la recréation de Activity ou sur Activity.onConfigurationChanged().