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 la police
  • Langue
  • Comparaison entre le mode sombre et le mode clair
  • Disponibilité du clavier

La plupart de ces changements de configuration sont dus à 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 sur mesure 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 nouvelle instance à l'aide de onCreate(), et cette nouvelle Activity instance 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 rechargeant automatiquement votre application avec d'autres ressources correspondant à la nouvelle configuration de l'appareil.

Exemple d'activité de loisirs

Prenons l'exemple d'une TextView qui affiche un titre statique à l'aide de android:text="@string/title", comme défini dans un fichier XML de mise en page. Lorsque la vue est créée, le texte est défini une seule 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 sur la valeur correcte en fonction de la nouvelle langue.

La recréation efface également tout état conservé sous forme de 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 instance entièrement nouvelle de Activity et de l'UI. De plus, l'ancienne Activity n'est plus visible ni valide. Par conséquent, toutes les références restantes à lui 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 préservé. 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 sera médiocre 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 exécutée au premier plan et en arrière-plan. Ces actions incluent :

  • Faire pivoter l'appareil
  • Activation du mode multifenêtre...
  • Redimensionnement de 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
  • Modifier 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 préserver l'état pertinent via la recréation d'Activity. L'option à utiliser dépend du type d'état que vous souhaitez conserver:

  • La persistance locale permet de gérer l'arrêt du processus pour les données complexes ou volumineuses. Le stockage local persistant inclut des bases de données ou des DataStore.
  • Objets conservés, tels que des instances ViewModel, pour gérer en mémoire l'état de l'UI lorsque l'utilisateur se sert activement de 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 à chacune de ces méthodes et sur leur utilisation appropriée, consultez la section Enregistrer les états de l'interface utilisateur.

Restreindre la recréation de l'activité

Vous pouvez empêcher la recréation automatique de l'activité pour certaines modifications de configuration. La recréation d'Activity entraîne la recréation de l'intégralité de l'interface utilisateur, ainsi que de tous les objets dérivés de Activity. Vous avez peut-être de bonnes raisons d'éviter cela. Par exemple, il est possible que votre application n'ait pas besoin de mettre à jour les ressources lors d'une modification de configuration spécifique ou que les performances soient limitées. Dans ce cas, vous pouvez déclarer que votre activité gère elle-même le changement 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> de votre fichier AndroidManifest.xml. Les valeurs possibles figurent dans la documentation de l'attribut android:configChanges.

Le code 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 de couleurs dynamiques introduit dans Android 12L (niveau d'API 32).

Réagir aux modifications 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 d'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 apporter les modifications ultérieures, 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 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 uniquement é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étaché.

Conserver l'état

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

  • Modifications inévitables:les modifications de configuration que vous ne pouvez pas empêcher peuvent redémarrer votre application.
  • Arrêt du processus:votre application doit pouvoir gérer l'arrêt de processus déclenché par le système. Si l'utilisateur quitte votre application et que celle-ci 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 modifications 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 toujours gérer correctement les modifications de configuration.

L'objet Configuration est disponible dans la hiérarchie de l'UI Compose avec la composition LocalConfiguration locale. Chaque fois que cela change, les fonctions modulables qui lisent des données à partir de LocalConfiguration.current se recomposent. Pour en savoir plus sur le fonctionnement local de la composition, 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 la 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 de Activity lorsque les paramètres régionaux changent, le Activity qui héberge 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 que vous devez connaître lorsque vous travaillez sur des modifications de configuration:

  • Configurations:les configurations d'appareil définissent la manière dont l'interface utilisateur s'affiche à l'utilisateur, comme la taille d'affichage de l'application, les paramètres régionaux ou le thème du système.
  • Modifications de la configuration:les configurations sont modifiées via une interaction de l'utilisateur. Par exemple, l'utilisateur peut modifier les paramètres de l'appareil ou la façon dont il interagit physiquement avec l'appareil. Il n'existe aucun moyen d'empêcher les modifications de configuration.
  • Recréation d'Activity:les modifications de configuration entraînent la recréation d'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 amène le système à détruire l'ancienne instance Activity et à en créer une nouvelle à la place. L'ancienne instance est désormais obsolète. Toute référence restante à ce fichier entraînera des fuites de mémoire, des bugs ou des plantages.
  • État:l'état dans 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 la section Enregistrer les états de l'interface utilisateur.
  • Désactiver:désactiver la recréation d'activités pour un type de modification de configuration est une optimisation potentielle. Elle nécessite que votre application se mette correctement à jour en réaction à la nouvelle configuration.

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

  • Préparez-vous à des modifications de configuration fréquentes:ne partez pas du principe que les modifications de configuration sont rares ou ne se produisent jamais, quel que soit le niveau d'API, le facteur de forme ou le kit d'interface utilisateur. Lorsqu'un utilisateur modifie une configuration, il s'attend à ce que les applications soient mises à 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 la section Enregistrer les états de l'interface utilisateur.
  • Évitez de désactiver cette fonctionnalité en guise de solution rapide:ne désactivez pas la recréation de Activity en tant que raccourci pour éviter la perte d'état. Pour désactiver la recréation de l'activité, vous devez respecter la promesse de gestion de la modification. Vous pouvez toujours perdre l'état en raison de la recréation d'Activity à partir 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 d'Activity. Conservez l'état comme décrit dans la section Enregistrer les états de l'interface utilisateur.
  • N'évitez pas les modifications de configuration:ne limitez pas l'orientation, le format ou le redimensionnement pour éviter les modifications de configuration et la recréation d'Activity. Cela a un impact négatif sur les utilisateurs qui souhaitent utiliser votre application de leur manière préférée.

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

Des modifications de configuration basées sur la taille peuvent se produire à tout moment et sont plus susceptibles d'être modifiées lorsque votre application s'exécute sur un appareil à grand écran où les utilisateurs peuvent passer en mode multifenêtre. Ils s'attendent à ce que votre application fonctionne bien 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 de ressources alternatives s'applique à la nouvelle configuration en raison d'une différence de taille d'écran, telle que la largeur, la hauteur ou la largeur minimale. Ces ressources incluent celles que l'application définit elle-même et celles de ses bibliothèques.

Restreindre la recréation d'activités pour les modifications de configuration basées sur 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 l'Activity. Au lieu de cela, il reçoit un appel à 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 est présent dans votre fichier manifeste.

Autoriser la recréation d'activités pour les modifications de configuration basées sur la taille

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

Voici quelques mises en garde à prendre en compte concernant les rappels Activity et View lorsque Activity n'est pas recréé:

  • Sur Android 11 (niveau d'API 30) à Android 13 (niveau d'API 33), Activity.onConfigurationChanged() n'est pas appelé.
  • Il existe un problème connu qui peut empêcher View.onConfigurationChanged() d'être appelé dans certains cas sur Android 12L (niveau d'API 32) et les premières versions d'Android 13 (niveau d'API 33). 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().