Kotlin Fundamentals 04.2: situations de cycle de vie complexes

Cet atelier de programmation fait partie du cours Android Kotlin Fundamentals. Vous tirerez le meilleur parti de ce cours si vous suivez les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont répertoriés sur la page de destination des ateliers de programmation Android Kotlin Fundamentals.

Présentation

Dans le dernier atelier de programmation, vous avez découvert les cycles de vie Activity et Fragment, et vous avez exploré les méthodes appelées lorsque l'état du cycle de vie change dans les activités et les fragments. Dans cet atelier de programmation, vous allez explorer le cycle de vie de l'activité plus en détail. Vous découvrirez également la bibliothèque de cycle de vie d'Android Jetpack, qui vous aide à gérer les événements de cycle de vie grâce à du code mieux organisé et plus facile à gérer.

Ce que vous devez déjà savoir

  • Qu'est-ce qu'une activité et comment en créer dans votre application.
  • Principes de base des cycles de vie Activity et Fragment, et rappels qui sont appelés lorsqu'une activité passe d'un état à un autre.
  • Comment remplacer les méthodes de rappel du cycle de vie onCreate() et onStop() pour effectuer des opérations à des moments différents de l'activité ou du cycle de vie du fragment

Points abordés

  • Configurer, démarrer et arrêter des parties de votre application dans les rappels de cycle de vie
  • Utiliser la bibliothèque de cycle de vie Android pour créer un observateur de cycle de vie et faciliter la gestion de l'activité et des fragments de cycle de vie
  • Incidence des coupures d'Android sur les données de votre application, et procédure d'enregistrement et de restauration automatique de ces données lorsque Android ferme votre application
  • Comment la rotation des appareils et d'autres modifications de configuration modifient l'état du cycle de vie et ont une incidence sur l'état de votre application

Objectifs de l'atelier

  • Modifiez l'application DessertClicker de manière à inclure une fonction de minuteur, et démarrez et arrêtez ce minuteur à différents moments du cycle de vie de l'activité.
  • Modifiez l'application pour qu'elle utilise la bibliothèque Cycle de vie Android et convertissez la classe DessertTimer en observateur de cycle de vie.
  • Configurez et utilisez Android Debug Bridge (adb) pour simuler l'arrêt du processus et les rappels de cycle de vie qui se produisent ensuite.
  • Implémentez la méthode onSaveInstanceState() pour conserver les données d'application qui seront perdues si l'application est fermée de manière inattendue. Ajoutez un code pour restaurer ces données au redémarrage de l'application.

Dans cet atelier de programmation, vous allez développer l'application DessertClicker de l'atelier de programmation précédent. Vous ajoutez un minuteur en arrière-plan, puis vous convertissez l'application afin d'utiliser la bibliothèque de cycle de vie Android.

Dans l'atelier de programmation précédent, vous avez appris à observer l'activité et les cycles de vie des fragments en ignorant divers rappels de cycle de vie et en enregistrant lorsque le système les appelle. Dans cette tâche, vous allez découvrir un exemple plus complexe de gestion des tâches de cycle de vie dans l'application DessertClicker. Vous allez utiliser un minuteur qui affiche une instruction de journal toutes les secondes et le nombre de secondes d'exécution.

Étape 1: Configurez des desserts

  1. Ouvrez l'application DessertClicker du dernier atelier de programmation. (Vous pouvez télécharger DessertClickerLogs ici si vous ne disposez pas de l'application.)
  2. Dans la vue Projet, développez java > com.example.android.dessertclicker et ouvrez DessertTimer.kt. Notez que, pour le moment, l'ensemble du code est commenté, et il ne s'exécute donc pas dans l'application.
  3. Sélectionnez tout le code dans la fenêtre de l'éditeur. Sélectionnez Code > Commenter avec un commentaire au trait ou appuyez sur Control+/ (Command+/ sur un Mac). Cette commande annule la mise en commentaire de l'ensemble du code du fichier. Il est possible qu'Android Studio affiche des erreurs de référence non résolues jusqu'à ce que vous recréez l'application.
  4. Notez que la classe DessertTimer inclut startTimer() et stopTimer(), qui démarrent et arrêtent le minuteur. Lorsque startTimer() est en cours d'exécution, le minuteur affiche un message de journal toutes les secondes, avec le nombre total de secondes d'exécution. À son tour, la méthode stopTimer() arrête le minuteur et les instructions de journalisation.
  1. Ouvrez MainActivity.kt. En haut de la classe, juste en dessous de la variable dessertsSold, ajoutez une variable pour le minuteur :
private lateinit var dessertTimer : DessertTimer;
  1. Faites défiler la page jusqu'à onCreate() et créez un objet DessertTimer, juste après l'appel de setOnClickListener():
dessertTimer = DessertTimer()


Maintenant que vous disposez d'un objet de minuteur, envisagez de démarrer et d'arrêter le minuteur pour qu'il ne s'exécute que lorsque l'activité s'affiche à l'écran. Vous allez examiner plusieurs options lors des prochaines étapes.

Étape 2: Lancez et arrêtez le minuteur

La méthode onStart() est appelée juste avant que l'activité ne soit visible. La méthode onStop() est appelée une fois que l'activité n'est plus visible. Ces rappels semblent être de bons choix pour démarrer et arrêter le minuteur.

  1. Dans la classe MainActivity, démarrez le minuteur du rappel onStart():
override fun onStart() {
   super.onStart()
   dessertTimer.startTimer()

   Timber.i("onStart called")
}
  1. Arrête le minuteur dans onStop() :
override fun onStop() {
   super.onStop()
   dessertTimer.stopTimer()

   Timber.i("onStop Called")
}
  1. Compilez et exécutez l'application. Dans Android Studio, cliquez sur le volet Logcat (Logcat). Dans le champ de recherche de Logcat, saisissez dessertclicker, qui filtrera en fonction des classes MainActivity et DessertTimer. Notez que l'application s'exécute immédiatement après le démarrage de l'application.
  2. Cliquez sur le bouton Retour et notez que le minuteur s'arrête à nouveau. Le minuteur s'arrête, car l'activité et le minuteur qu'il contrôle ont été détruits.
  3. Utilisez l'écran des événements récents pour revenir à l'application. Dans Logcat, notez que le minuteur redémarre à partir de 0.
  4. Cliquez sur le bouton Partager. Notez dans Logcat que le minuteur est toujours en cours d'exécution.

  5. Cliquez sur le bouton Accueil. Notez dans Logcat que le minuteur s'arrête.
  6. Utilisez l'écran des événements récents pour revenir à l'application. Dans Logcat, notez que le minuteur redémarre là où il s'était arrêté.
  7. Dans MainActivity, dans la méthode onStop(), commentez l'appel de stopTimer(). Le commentaire stopTimer() illustre le cas où vous démarrez une opération dans onStart(), mais oubliez de l'arrêter à nouveau dans onStop().
  8. Compilez et exécutez l'application, puis cliquez sur le bouton d'accueil après le démarrage du minuteur. Même si l'application est en arrière-plan, le minuteur s'exécute et utilise continuellement les ressources du système. Le minuteur continue de s'exécuter, car il s'agit d'une fuite de mémoire pour votre application. Ce n'est probablement pas le comportement que vous souhaitez appliquer.

    Lorsque vous configurez ou démarrez un rappel, vous risquez d'arrêter ou de supprimer le rappel associé. Vous éviterez ainsi toute exécution inutile.
  1. Annulez la mise en commentaire de la ligne dans onStop() où vous arrêtez le minuteur.
  2. Couper et coller l'appel startTimer() de onStart() vers onCreate() Cette modification illustre le cas où vous initialisez et démarrez une ressource dans onCreate(), plutôt que d'utiliser onCreate() pour l'initialiser et onStart() pour la démarrer.
  3. Compilez et exécutez l'application. Notez que le minuteur se lance comme prévu.
  4. Cliquez sur "Home" (Accueil) pour arrêter l'application. Le minuteur s'arrête comme prévu.
  5. Utilisez l'écran des événements récents pour revenir à l'application. Notez que le minuteur ne redémarre pas dans ce cas, car onCreate() n'est appelé que lorsque l'application démarre. Il n'est pas appelé lorsqu'une application revient au premier plan.

Points importants à retenir:

  • Lorsque vous configurez une ressource dans un rappel de cycle de vie, vous devez également la supprimer.
  • Configurez et supprimez les méthodes correspondantes.
  • Si vous avez configuré un élément dans onStart(), arrêtez-le ou supprimez-le à nouveau dans onStop().

Dans l'application DessertClicker, vous pouvez facilement constater que si vous avez lancé le minuteur dans onStart(), vous devez l'arrêter dans onStop(). Il n'y a qu'un seul minuteur, c'est pourquoi il est difficile de le retenir.

Dans une application Android plus complexe, vous pouvez configurer de nombreux éléments dans onStart() ou onCreate(), puis tout supprimer dans onStop() ou onDestroy(). Par exemple, vous pouvez avoir besoin de configurer ou de supprimer, d'arrêter et d'arrêter des animations, de la musique, des capteurs ou des minuteurs. Si vous en oubliez une, vous risquez de rencontrer des bugs et de vous demander des inquiétudes.

La bibliothèque de cycle de vie, qui fait partie d'Android Jetpack, simplifie cette tâche. La bibliothèque est particulièrement utile lorsque vous devez suivre de nombreuses pièces mobiles, dont certaines ont des états différents. La bibliothèque modifie le fonctionnement des cycles de vie: généralement l'activité ou le fragment indique à un composant (par exemple, DessertTimer) comment procéder lorsqu'un rappel de cycle de vie se produit. Toutefois, lorsque vous utilisez la bibliothèque de cycle de vie, le composant lui-même surveille les changements de cycle de vie, puis effectue les actions nécessaires lorsque ces changements se produisent.

La bibliothèque de cycle de vie comprend trois parties principales:

  • Propriétaires du cycle de vie : les composants qui ont (et donc, possèdent) des cycles de vie. Activity et Fragment sont propriétaires du cycle de vie. Les propriétaires de cycle de vie implémentent l'interface LifecycleOwner.
  • La classe Lifecycle, qui contient l'état réel d'un propriétaire du cycle de vie et déclenche des événements lorsque des changements de cycle de vie se produisent.
  • Des observateurs de cycle de vie, qui observent l'état du cycle de vie et effectuent des tâches lorsque celui-ci change Les observateurs du cycle de vie implémentent l'interface LifecycleObserver.

Dans cette tâche, vous allez convertir l'application DessertClicker afin d'utiliser la bibliothèque de cycle de vie Android. Vous découvrirez également comment la bibliothèque facilite l'utilisation des cycles de vie d'activité et de fragment Android.

Étape 1: Transformez DessertMinuteur en Cycle de vie

Un élément clé de la bibliothèque de cycle de vie est le concept d'observation du cycle de vie. L'observation permet aux classes (comme DessertTimer) de connaître l'activité ou le cycle de vie d'un fragment, puis de s'arrêter et de s'arrêter en réponse aux changements de ces états. Avec un observateur de cycle de vie, vous pouvez arrêter de démarrer et d'arrêter des objets à partir des méthodes d'activité et de fragment.

  1. Ouvrez la classe DesertTimer.kt.
  2. Modifiez la signature de la classe DessertTimer comme suit :
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {

Cette nouvelle définition de classe a deux conséquences:

  • Le constructeur utilise un objet Lifecycle, qui correspond au cycle de vie observé par le minuteur.
  • La définition de classe implémente l'interface LifecycleObserver.
  1. Sous la variable runnable, ajoutez un bloc init à la définition de classe. Dans le bloc init, utilisez la méthode addObserver() pour connecter l'objet de cycle de vie transmis du propriétaire (l'activité) à cette classe (l'observateur).
 init {
   lifecycle.addObserver(this)
}
  1. Annoter startTimer() avec @OnLifecycleEvent annotation et utiliser l'événement de cycle de vie ON_START. Tous les événements de cycle de vie que l'observateur peut observer se trouvent dans la classe Lifecycle.Event.
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {
  1. Procédez de la même manière pour stopTimer(), à l'aide de l'événement ON_STOP:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()

Étape 2: Modifiez MainActivity

Votre classe MainActivity est déjà propriétaire du cycle de vie via l'héritage, car la super-classe FragmentActivity implémente LifecycleOwner. Par conséquent, vous n'avez rien à faire pour que votre activité soit sensible au cycle de vie. Il vous suffit de transmettre l'objet de cycle de vie de l'activité au constructeur DessertTimer.

  1. Ouvrez MainActivity. Dans la méthode onCreate(), modifiez l'initialisation de DessertTimer pour inclure this.lifecycle :
dessertTimer = DessertTimer(this.lifecycle)

La propriété lifecycle de l'activité contient l'objet Lifecycle dont elle est propriétaire.

  1. Supprimez l'appel de startTimer() dans onCreate() et l'appel de stopTimer() dans onStop(). Vous n'avez plus besoin d'indiquer à DessertTimer comment procéder dans l'activité. En effet, DessertTimer observe maintenant le cycle de vie lui-même et est automatiquement alerté lorsqu'il change d'état. Vous n'avez plus qu'à consigner un message dans ces rappels.
  2. Compilez et exécutez l'application, puis ouvrez le fichier Logcat. Notez que le minuteur a démarré, comme prévu.
  3. Cliquez sur le bouton d'accueil pour passer l'application en arrière-plan. Vous remarquerez que le minuteur s'est arrêté comme prévu.

Qu'advient-il de votre application et de ses données si Android la ferme en arrière-plan ? Il est important de bien comprendre ce cas difficile.

Lorsque votre application passe en arrière-plan, elle n'est pas détruite, elle s'interrompt simplement et attend que l'utilisateur y retourne. Toutefois, l'une des principales préoccupations de l'OS Android est de garantir le bon déroulement de l'activité au premier plan. Par exemple, si votre utilisateur utilise une application GPS pour l'aider à prendre un bus, il est important d'afficher cette application rapidement et de continuer à afficher l'itinéraire. Il est moins important de garder l'application DessertClicker, que l'utilisateur n'a peut-être pas regardée depuis plusieurs jours, en arrière-plan.

Android régule les applications en arrière-plan de sorte qu'elles puissent fonctionner sans problème. Par exemple, Android limite la durée de traitement des applications exécutées en arrière-plan.

Parfois, Android arrête même un processus d'application complet, y compris toute activité associée à l'application. Android effectue ce type d'arrêt lorsque le système est stressé et risque d'être retardé. Par conséquent, aucun rappel ou code supplémentaire n'est exécuté à ce stade. Le processus s'arrête tout simplement en arrière-plan. mais l'utilisateur ne semble pas avoir fermé l'application. Lorsque l'utilisateur revient à une application arrêtée par l'OS Android, Android la redémarre.

Dans cette tâche, vous allez simuler un arrêt d'un processus Android et examiner ce qu'il advient de votre application au redémarrage.

Étape 1: Utilisez adb pour simuler l'arrêt d'un processus

Android Debug Bridge (adb) est un outil de ligne de commande qui vous permet d'envoyer des instructions aux émulateurs et aux appareils connectés à votre ordinateur. Lors de cette étape, vous allez utiliser adb pour fermer le processus de votre application et voir ce qui se passe lorsque Android l'arrête.

  1. Compilez et exécutez votre application. Cliquez plusieurs fois sur le cupcake.
  2. Appuyez sur le bouton d'accueil pour mettre votre application en arrière-plan. Votre application est maintenant arrêtée, et elle peut être fermée si Android a besoin des ressources qu'elle utilise.
  3. Dans Android Studio, cliquez sur l'onglet Terminal pour ouvrir le terminal de ligne de commande.
  4. Saisissez adb et appuyez sur Entrée.

    Si vous obtenez de nombreux résultats commençant par Android Debug Bridge version X.XX.X et se terminant par tags to be used by logcat (see logcat —help, tout va bien. Si, à la place, adb: command not found s'affiche, vérifiez que la commande adb est disponible dans votre chemin d'exécution. Pour obtenir des instructions, consultez la section "Ajouter adb à votre chemin d'exécution" dans le chapitre Utilitaires.
  5. Copiez et collez ce commentaire dans la ligne de commande et appuyez sur Entrée:
    .
adb shell am kill com.example.android.dessertclicker

Cette commande indique à tous les appareils ou émulateurs connectés d'arrêter le processus avec le nom de package dessertclicker, mais uniquement si l'application est exécutée en arrière-plan. Comme votre application était en arrière-plan, rien ne s'affiche sur l'écran de l'appareil ni de l'émulateur pour vous indiquer que le processus a été arrêté. Dans Android Studio, cliquez sur l'onglet Run (Exécuter) pour afficher le message "& term-ed application" (Application arrêtée). Cliquez sur l'onglet Logcat (Logcat) pour vérifier que le rappel onDestroy() n'a jamais été exécuté. Votre activité s'est simplement terminée.

  1. Utilisez l'écran "Récents" pour revenir à l'application. Votre application apparaît récemment, si elle a été mise en arrière-plan ou si elle a été complètement arrêtée. Lorsque vous revenez à l'application depuis l'écran des applications récentes, l'activité redémarre. L'activité suit l'ensemble des rappels de cycle de vie au démarrage, y compris onCreate().
  2. Notez que lorsque vous redémarrez l'application, votre "score" est réinitialisé (nombre de desserts vendus et montant total) par défaut (0). Si Android a arrêté votre appli, pourquoi n'a-t-elle pas enregistré son état ?

    Lorsque l'OS redémarre votre appli, Android essaie au mieux de réinitialiser l'appli. Android affiche l'état de certaines de vos vues pour les enregistrer dans un groupe lorsque vous quittez l'activité. Voici quelques exemples de données enregistrées automatiquement : le texte dans un TextText (à condition qu'il ait un ID défini dans la mise en page) et la pile "Retour" de votre activité.

    Toutefois, il peut arriver qu'Android ne connaisse pas toutes vos données. Par exemple, si vous avez une variable personnalisée telle que revenue dans l'application DessertClicker, l'OS Android n'a pas connaissance de ces données ni de leur importance pour votre activité. Vous devez ajouter vous-même ces données au lot.

Étape 2: Utilisez onSaveInstanceState() pour enregistrer les données du bundle

La méthode onSaveInstanceState() est le rappel que vous utilisez pour enregistrer les données dont vous pourriez avoir besoin si l'OS Android détruit votre application. Dans le schéma du rappel de cycle de vie, onSaveInstanceState() est appelé après l'arrêt de l'activité. Il est appelé à chaque fois que votre application passe en arrière-plan.

Considérez l'appel onSaveInstanceState() comme une mesure de sécurité. Il vous permet d'enregistrer une petite quantité d'informations dans un groupe lorsque votre activité quitte le premier plan. Le système enregistre maintenant ces données, car il se peut que l'OS subit une pression sur les ressources avant d'avoir arrêté l'application. Chaque fois que vous enregistrez les données, celles-ci peuvent être restaurées, si nécessaire.

  1. Dans MainActivity, ignorez le rappel onSaveInstanceState() et ajoutez une instruction de journal Timber.
override fun onSaveInstanceState(outState: Bundle) {
   super.onSaveInstanceState(outState)

   Timber.i("onSaveInstanceState Called")
}
  1. Compilez et exécutez l'application, puis cliquez sur le bouton Accueil pour la mettre en arrière-plan. Notez que le rappel onSaveInstanceState() se produit juste après onPause() et onStop():
  2. En haut du fichier, juste avant la définition de la classe, ajoutez les constantes suivantes:
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
const val KEY_TIMER_SECONDS = "timer_seconds_key"

Vous utiliserez ces clés pour enregistrer et récupérer les données du groupe d'états de l'instance.

  1. Faites défiler la page jusqu'à onSaveInstanceState() et observez le paramètre outState, de type Bundle.

    Un groupe est une collection de paires clé-valeur, où les clés sont toujours des chaînes. Vous pouvez indiquer des valeurs primitives telles que int et boolean dans le groupe.
    Comme le système conserve ce groupe en mémoire RAM, il est recommandé de réduire la taille des données qu'il contient. La taille de ce lot est également limitée, mais sa taille varie d'un appareil à l'autre. Généralement, vous devez stocker beaucoup moins de 100 000 fichiers, sinon vous risquez de faire planter votre application avec l'erreur TransactionTooLargeException.
  2. Dans onSaveInstanceState(), insérez la valeur revenue (un entier) dans le groupe avec la méthode putInt():
outState.putInt(KEY_REVENUE, revenue)

La méthode putInt() (et les méthodes similaires de la classe Bundle comme putFloat() et putString()) utilisent deux arguments: une chaîne pour la clé (la constante KEY_REVENUE) et la valeur réelle à enregistrer.

  1. Effectuez la même procédure avec le nombre de desserts vendus et l'état du minuteur:
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
outState.putInt(KEY_TIMER_SECONDS, dessertTimer.secondsCount)

Étape 3: Utilisez onCreate() pour restaurer les données du bundle

  1. Faites défiler la page jusqu'à onCreate() et examinez la signature de la méthode:
override fun onCreate(savedInstanceState: Bundle) {

Notez que onCreate() obtient une Bundle à chaque appel. Lorsque votre activité est redémarrée en raison d'un arrêt d'un processus, le groupe que vous avez enregistré est transmis à onCreate(). Si votre activité était lancée récemment, ce groupe dans onCreate() est null. Ainsi, si l'activité n'est pas null, vous savez que vous recréez votre activité.

  1. Ajoutez ce code à onCreate(), après la configuration de DessertTimer:
if (savedInstanceState != null) {
   revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}

Le test pour null détermine s'il existe des données dans le bundle ou si le bundle est null, ce qui vous indique si l'appli a été redémarrée ou a été recréée après un arrêt. Ce test est une pratique courante pour restaurer des données à partir du bundle.

Notez que la clé que vous avez utilisée ici (KEY_REVENUE) est la même que celle que vous avez utilisée pour putInt(). Pour vous assurer d'utiliser la même clé à chaque fois, nous vous recommandons de définir ces clés comme constantes. Vous utilisez getInt() pour récupérer les données du groupe, tout comme vous avez utilisé putInt() pour saisir les données dans le groupe. La méthode getInt() comporte deux arguments:

  • Chaîne faisant office de clé, par exemple "key_revenue" pour la valeur du chiffre d'affaires.
  • Valeur par défaut si aucune valeur n'existe pour cette clé dans le groupe.

Le nombre entier issu du groupe est ensuite attribué à la variable revenue, et cette valeur est utilisée dans l'interface utilisateur.

  1. Ajoutez les méthodes getInt() pour restaurer le nombre de desserts vendus et la valeur du minuteur:
if (savedInstanceState != null) {
   revenue = savedInstanceState.getInt(KEY_REVENUE, 0)dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
   dessertTimer.secondsCount =
       savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
}
  1. Compilez et exécutez l'application. Appuyez sur le cupcake au moins cinq fois jusqu'à ce qu'il passe à un donut. Cliquez sur "Home" (Accueil) pour mettre l'application en arrière-plan.
  2. Dans l'onglet Terminal d'Android Studio, exécutez adb pour arrêter le processus de l'application.
adb shell am kill com.example.android.dessertclicker
  1. Utilisez l'écran des récents contenus pour revenir à l'application. Sachez que cette fois, l'application affiche les valeurs de revenus et de desserts vendues par le lot. Mais vous remarquerez aussi que le dessert est de retour dans un cupcake. Il vous reste une dernière tâche à accomplir pour vous assurer que l'application revient après un arrêt exactement comme elle l'avait été.
  2. Dans MainActivity, examinez la méthode showCurrentDessert(). Notez que cette méthode détermine l'image de dessert à afficher dans l'activité en fonction du nombre actuel de desserts vendus et de la liste de desserts de la variable allDesserts.
for (dessert in allDesserts) {
   if (dessertsSold >= dessert.startProductionAmount) {
       newDessert = dessert
   }
    else break
}

Cette méthode repose sur le nombre de desserts vendus afin de choisir l'image appropriée. Vous n'avez donc rien à faire pour stocker une référence à l'image dans le groupe onSaveInstanceState(). Dans ce lot, vous stockez déjà le nombre de desserts vendus.

  1. Dans onCreate(), dans le bloc qui rétablit l'état du bundle, appelez showCurrentDessert() :
 if (savedInstanceState != null) {
   revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
   dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
   dessertTimer.secondsCount = 
      savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
   showCurrentDessert()                   
}
  1. Compilez et exécutez l'application, puis placez-la en arrière-plan. Utilisez adb pour arrêter le processus. Utilisez l'écran des événements récents pour revenir à l'application. Sachez que les valeurs concernant les desserts indiqués, le total des revenus et l'image du dessert sont correctement restaurées.

Un dernier cas particulier est nécessaire pour gérer l'activité et le cycle de vie des fragments. Il est important de bien comprendre l'impact des modifications de configuration sur le cycle de vie de vos activités et fragments.

Une modification de la configuration se produit lorsque l'état de l'appareil change de manière tellement radicale que le moyen le plus simple pour résoudre le changement consiste à arrêter complètement et à recréer l'activité. Par exemple, s'il modifie la langue de l'appareil, la mise en page devra peut-être être modifiée pour s'adapter aux différentes directions d'affichage du texte. Si l'utilisateur branche l'appareil sur une station d'accueil ou ajoute un clavier physique, la mise en page de l'application devra peut-être profiter d'une taille ou d'une mise en page différentes. En outre, si l'appareil passe de l'orientation portrait à l'arrière-plan ou inversement, la mise en page devra peut-être être modifiée en fonction de la nouvelle orientation.

Étape 1: Explorez la rotation des appareils et les rappels de cycle de vie

  1. Compilez et exécutez votre application, puis ouvrez Logcat.
  2. Faites pivoter l'appareil ou l'émulateur en mode Paysage. Vous pouvez faire pivoter l'émulateur à gauche ou à droite à l'aide des boutons de rotation, ou à l'aide des touches Control et fléchées (Command et des flèches) sur un Mac.
  3. Examinez la sortie dans Logcat. Filtrez le résultat sur MainActivity.
    Notez que lorsque l'appareil ou l'émulateur fait pivoter l'écran, le système appelle tous les rappels de cycle de vie pour arrêter l'activité. Ensuite, lorsque l'activité est recréée, le système appelle tous les rappels de cycle de vie pour démarrer l'activité.
  4. Dans MainActivity, mettez en commentaire toute la méthode onSaveInstanceState().
  5. Compilez et exécutez à nouveau votre application. Cliquez plusieurs fois sur le cupcake, puis faites pivoter l'appareil ou l'émulateur. Cette fois, lorsque vous faites pivoter l'appareil et que l'activité est arrêtée, puis recréée, l'activité démarre avec des valeurs par défaut.

    Lorsqu'une modification de configuration se produit, Android utilise le même groupe d'états d'instance que vous avez appris dans la tâche précédente pour enregistrer et restaurer l'état de l'application. Comme pour un arrêt de processus, utilisez onSaveInstanceState() pour placer les données de votre application dans le bundle. Ensuite, restaurez les données dans onCreate() pour éviter de perdre des données d'état d'activité si l'appareil est tourné.
  6. Dans MainActivity, annulez la mise en commentaire de la méthode onSaveInstanceState(), exécutez l'application, cliquez sur le cupcake et faites-la pivoter l'application ou l'appareil. Cette fois, les données du dessert sont conservées pendant la rotation de l'activité.

Projet Android Studio: DessertClickerFinal

Conseils sur le cycle de vie

  • Si vous avez configuré ou démarré un rappel dans le cycle de vie, arrêtez ou supprimez cette action dans le rappel correspondant. En arrêtant ce qui se passe, vous vous assurez qu'il ne sera plus utilisé quand il ne sera plus nécessaire. Par exemple, si vous définissez un minuteur dans onStart(), vous devez le suspendre ou l'arrêter dans onStop().
  • Utilisez onCreate() uniquement pour initialiser les parties de votre application qui s'exécutent une fois, au démarrage de l'application. Utilisez onStart() pour démarrer les parties de votre application qui s'exécutent au démarrage et à chaque fois qu'elle retourne au premier plan.

Bibliothèque de cycle de vie

  • Utilisez la bibliothèque de cycle de vie Android pour remplacer le contrôle du cycle de vie de l'activité ou du fragment par le composant lui-même sensible au cycle de vie.
  • Les propriétaires de cycle de vie sont des composants qui ont des cycles de vie (et donc les propres) tels que Activity et Fragment. Les propriétaires de cycle de vie implémentent l'interface LifecycleOwner.
  • Les observateurs du cycle de vie consultent l'état actuel du cycle de vie et effectuent des tâches lorsque celui-ci change. Les observateurs du cycle de vie implémentent l'interface LifecycleObserver.
  • Les objets Lifecycle contiennent les états réels du cycle de vie et déclenchent des événements lorsque le cycle de vie change.

Pour créer une classe prenant en compte le cycle de vie, procédez comme suit:

  • Implémentez l'interface LifecycleObserver dans les classes qui doivent tenir compte du cycle de vie.
  • Initialisez une classe d'observateur de cycle de vie avec l'objet de cycle de vie de l'activité ou du fragment.
  • Dans la classe d'observateur du cycle de vie, annotez les méthodes compatibles avec ce cycle avec le changement de l'état du cycle qui les intéresse.

    Par exemple, l'annotation @OnLifecycleEvent(Lifecycle.Event.ON_START) indique que la méthode surveille l'événement de cycle de vie onStart.

Traitement des coupures et enregistrement de l'état d'activité

  • Android régule les applications exécutées en arrière-plan de sorte qu'elles puissent fonctionner sans problème. Ces réglementations imposent des restrictions en ce qui concerne le traitement des applications en arrière-plan, voire l'arrêt de l'ensemble du processus d'applications.
  • L'utilisateur ne peut pas savoir si le système a arrêté une application en arrière-plan. L'application s'affiche toujours sur l'écran des applications récentes et doit redémarrer dans l'état où l'utilisateur l'a laissée.
  • Android Debug Bridge (adb) est un outil de ligne de commande qui vous permet d'envoyer des instructions aux émulateurs et aux appareils connectés à votre ordinateur. Vous pouvez utiliser adb pour simuler un arrêt d'un processus dans votre application.
  • Lorsque Android arrête le processus de votre application, la méthode de cycle de vie onDestroy() n'est pas appelée. L'appli s'arrête.

Conserver l'activité et l'état du fragment

  • Lorsque votre application passe en arrière-plan, juste après l'appel de onStop(), les données de l'application sont enregistrées dans un groupe. Certaines données d'application telles que le contenu d'une EditText sont automatiquement enregistrées.
  • Le groupe est une instance de Bundle, qui est une collection de clés et de valeurs. Les clés sont toujours des chaînes.
  • Utilisez le rappel onSaveInstanceState() pour enregistrer d'autres données dans le groupe que vous souhaitez conserver, même si l'application a été arrêtée automatiquement. Pour saisir des données dans le groupe, utilisez les méthodes du groupe qui commencent par put, par exemple putInt().
  • Vous pouvez récupérer les données du groupe dans la méthode onRestoreInstanceState() ou, plus généralement, dans onCreate(). La méthode onCreate() comporte un paramètre savedInstanceState qui contient le groupe.
  • Si la variable savedInstanceState contient null, l'activité a commencé sans groupe d'états et il n'existe aucune donnée d'état à récupérer.
  • Pour récupérer les données du groupe à l'aide d'une clé, utilisez les méthodes Bundle qui commencent par get, par exemple getInt().

Modifications de configuration

  • Une modification de la configuration se produit lorsque l'état de l'appareil change de manière tellement radicale que le moyen le plus simple pour résoudre le changement consiste à arrêter et à recréer l'activité.
  • L'exemple de modification de configuration le plus courant est lorsque l'utilisateur fait pivoter l'appareil du mode portrait au mode paysage, ou du mode paysage au mode portrait. Une modification de la configuration peut également se produire lorsque la langue de l'appareil change ou que le clavier matériel est branché.
  • Lorsqu'une modification de configuration se produit, Android appelle tous les rappels d'arrêt liés au cycle de vie de l'activité. Android redémarre ensuite l'activité à partir de zéro en exécutant tous les rappels de démarrage du cycle de vie.
  • Lorsque Android arrête une application en raison d'une modification de la configuration, il redémarre l'activité avec le bundle d'état disponible pour onCreate().
  • Comme pour l'arrêt du processus, enregistrez l'état de votre application dans le groupe dans onSaveInstanceState().

Cours Udacity:

Documentation pour les développeurs Android:

Autre :

Cette section répertorie les devoirs possibles pour les élèves qui effectuent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. C'est à l'enseignant de procéder comme suit:

  • Si nécessaire, rendez-les.
  • Communiquez aux élèves comment rendre leurs devoirs.
  • Notez les devoirs.

Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ils n'ont pas besoin d'attribuer les devoirs de leur choix.

Si vous suivez vous-même cet atelier de programmation, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.

Modifier une application

Ouvrez l'application DiceRoller de la leçon 1. (Vous pouvez télécharger l'application si vous ne l'avez pas installée.) Compilez et exécutez l'application. Notez que si vous faites pivoter l'appareil, la valeur actuelle du dé est perdue. Intégrez onSaveInstanceState() pour conserver cette valeur dans le groupe et restaurer cette valeur dans onCreate().

Répondez à ces questions.

Question 1

Votre application contient une simulation physique qui nécessite un calcul complexe. L'utilisateur reçoit alors un appel téléphonique. Parmi ces affirmations, laquelle est exacte ?

  • Pendant l'appel téléphonique, vous devez continuer à calculer la position des objets dans la simulation physique.
  • Lors de l'appel téléphonique, vous devez cesser de calculer les positions des objets dans la simulation physique.

Question 2

Quelle méthode de cycle de vie devez-vous remplacer pour suspendre la simulation lorsque l'application ne s'affiche pas à l'écran ?

  • onDestroy()
  • onStop()
  • onPause()
  • onSaveInstanceState()

Question 3

Quelle interface la classe doit-elle mettre en œuvre pour qu'elle puisse prendre en compte le cycle de vie dans la bibliothèque Android ?

  • Lifecycle
  • LifecycleOwner
  • Lifecycle.Event
  • LifecycleObserver

Question 4

Dans quels cas la méthode onCreate() dans votre activité reçoit-elle un Bundle avec des données (c'est-à-dire que Bundle n'est pas null) ? Plusieurs réponses peuvent s'appliquer.

  • L'activité est redémarrée après la rotation de l'appareil.
  • L'activité a démarré de zéro.
  • L'activité est réactivée après un retour en arrière-plan.
  • L'appareil est redémarré.

Démarrez la leçon suivante: 5.1: ViewModel et ViewModelFactory.

Pour obtenir des liens vers d'autres ateliers de programmation dans ce cours, consultez la page de destination des ateliers de programmation Android Kotlin Fundamentals.