Cet atelier de programmation fait partie du cours Principes de base d'Android en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.
Introduction
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 d'une activité plus en détail. Vous en apprendrez également davantage sur la bibliothèque de cycle de vie d'Android Jetpack, qui peut vous aider à gérer les événements de cycle de vie avec un 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 une dans votre application ?
- Principes de base des cycles de vie
ActivityetFragment, et rappels appelés lorsqu'une activité passe d'un état à un autre - Remplacement des méthodes de rappel de cycle de vie
onCreate()etonStop()pour effectuer des opérations à différents moments du cycle de vie de l'activité ou du fragment
Points abordés
- Comment 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 du cycle de vie des activités et des fragments
- Découvrez comment les arrêts de processus Android affectent les données de votre application, et comment enregistrer et restaurer ces données automatiquement lorsque Android ferme votre application.
- Comment la rotation de l'appareil et d'autres changements de configuration modifient les états du cycle de vie et affectent l'état de votre application.
Objectifs de l'atelier
- Modifier l'application DessertClicker pour inclure une fonction de minuteur, et démarrer et arrêter ce minuteur à différents moments du cycle de vie de l'activité
- Modifiez l'application pour qu'elle utilise la bibliothèque de cycle de vie Android et convertissez la classe
DessertTimeren observateur de cycle de vie. - Configurez et utilisez Android Debug Bridge (
adb) pour simuler l'arrêt du processus de votre application et les rappels de cycle de vie qui se produisent alors. - Implémenter la méthode
onSaveInstanceState()pour conserver les données de l'application qui pourraient être perdues en cas de fermeture inattendue de l'application. Ajouter un code pour restaurer ces données lorsque l'application redémarrera
Dans cet atelier de programmation, vous allez développer l'application DessertClicker de l'atelier de programmation précédent. Vous ajoutez un minuteur d'arrière-plan, puis convertissez l'application pour qu'elle utilise la bibliothèque de cycle de vie Android.

Dans l'atelier de programmation précédent, vous avez appris à observer les cycles de vie des activités et des fragments en remplaçant divers rappels de cycle de vie et en enregistrant le moment où le système appelle ces rappels. Dans cette tâche, vous allez explorer 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 journalisation toutes les secondes, avec le nombre de secondes écoulées depuis son lancement.
Étape 1 : Configurez DessertTimer
- Ouvrez l'application DessertClicker du dernier atelier de programmation. (Vous pouvez télécharger DessertClickerLogs ici si vous n'avez pas l'application.)
- Dans la vue Project (Projet), développez java > com.example.android.dessertclicker, puis ouvrez
DessertTimer.kt. Notez que pour le moment, tout le code est mis en commentaire, il ne s'exécute donc pas dans l'application. - Sélectionnez tout le code dans la fenêtre de l'éditeur. Sélectionnez Code > Commentaire avec commentaire de ligne ou appuyez sur
Control+/(Command+/sur un Mac). Cette commande décommente tout le code du fichier. (Android Studio peut afficher des erreurs de référence non résolues tant que vous n'avez pas recréé l'application.) - Notez que la classe
DessertTimerinclutstartTimer()etstopTimer(), qui démarrent et arrêtent le minuteur. LorsquestartTimer()est en cours d'exécution, le minuteur affiche un message de journal toutes les secondes, avec le nombre total de secondes écoulées. La méthodestopTimer()arrête à son tour le minuteur et les instructions de journalisation.
- Ouvrez
MainActivity.kt. En haut de la classe, juste en dessous de la variabledessertsSold, ajoutez une variable pour le minuteur :
private lateinit var dessertTimer : DessertTimer;- Faites défiler l'écran jusqu'à
onCreate()et créez un objetDessertTimer, juste après l'appel àsetOnClickListener():
dessertTimer = DessertTimer()
Maintenant que vous avez un objet de minuteur de dessert, réfléchissez à l'endroit où vous devez démarrer et arrêter le minuteur pour qu'il ne fonctionne que lorsque l'activité est à l'écran. Vous allez examiner quelques options dans les prochaines étapes.
Étape 2 : Démarrer et arrêter le minuteur
La méthode onStart() est appelée juste avant que l'activité ne devienne visible. La méthode onStop() est appelée une fois que l'activité n'est plus visible. Ces rappels semblent être de bons candidats pour démarrer et arrêter le minuteur.
- Dans la classe
MainActivity, démarrez le minuteur dans le rappelonStart():
override fun onStart() {
super.onStart()
dessertTimer.startTimer()
Timber.i("onStart called")
}- Arrête le minuteur dans
onStop():
override fun onStop() {
super.onStop()
dessertTimer.stopTimer()
Timber.i("onStop Called")
}- Compilez et exécutez l'application. Dans Android Studio, cliquez sur le volet Logcat. Dans le champ de recherche Logcat, saisissez
dessertclickerpour filtrer les classesMainActivityetDessertTimer. Notez qu'une fois l'application lancée, le minuteur démarre également immédiatement.
- Cliquez sur le bouton Retour et remarquez 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.
- Utilisez l'écran des actions récentes pour revenir à l'application. Notez que dans Logcat, le minuteur redémarre à zéro.
- Cliquez sur le bouton Partager. Notez que dans Logcat, le minuteur est toujours en cours d'exécution.

- Cliquez sur le bouton Home (Accueil). Notez que dans Logcat, le minuteur cesse de fonctionner.
- Utilisez l'écran des actions récentes pour revenir à l'application. Notez que dans Logcat, le minuteur redémarre là où il s'était arrêté.
- Dans
MainActivity, dans la méthodeonStop(), mettez en commentaire l'appel àstopTimer(). La mise en commentaire destopTimer()illustre le cas où vous démarrez une opération dansonStart(), mais oubliez de l'arrêter dansonStop(). - Compilez et exécutez l'application, puis cliquez sur le bouton "Accueil" une fois le minuteur démarré. Même si l'application est en arrière-plan, le minuteur fonctionne et utilise en permanence les ressources système. Si le minuteur continue de fonctionner, cela constitue une fuite de mémoire pour votre application et n'est probablement pas le comportement souhaité.
En règle générale, lorsque vous configurez ou démarrez quelque chose dans un rappel, vous l'arrêtez ou le supprimez dans le rappel correspondant. Vous évitez ainsi d'avoir des éléments en cours d'exécution lorsque vous n'en avez plus besoin.
- Annulez la mise en commentaire de la ligne dans
onStop()où vous arrêtez le minuteur. - Coupez et collez l'appel
startTimer()deonStart()àonCreate(). Ce changement montre le cas où vous initialisez et démarrez une ressource dansonCreate(), plutôt que d'utiliseronCreate()pour l'initialiser etonStart()pour la démarrer. - Compilez et exécutez l'application. Vous remarquerez que le minuteur se lance, comme prévu.
- Cliquez sur "Home" (Accueil) pour arrêter l'application. Le minuteur s'arrête, comme prévu.
- Utilisez l'écran des actions récentes 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, et non lorsqu'elle revient au premier plan.
Points importants à retenir :
- Lorsque vous configurez une ressource dans un rappel de cycle de vie, démontez-la également.
- Effectuez la configuration et la suppression dans les méthodes correspondantes.
- Si vous configurez quelque chose dans
onStart(), arrêtez-le ou supprimez-le dansonStop().
Dans l'application DessertClicker, il est assez facile de voir que si vous avez démarré le minuteur dans onStart(), vous devez l'arrêter dans onStop(). Il n'y a qu'un seul minuteur, il est donc facile de se souvenir de la façon de l'arrêter.
Dans une application Android plus complexe, vous pouvez configurer de nombreux éléments dans onStart() ou onCreate(), puis les supprimer tous dans onStop() ou onDestroy(). Par exemple, vous pouvez avoir des animations, de la musique, des capteurs ou des minuteurs que vous devez à la fois configurer et arrêter, et démarrer et arrêter. Si vous en oubliez un, cela peut entraîner des bugs et des maux de tête.
La bibliothèque Lifecycle, qui fait partie d'Android Jetpack, simplifie cette tâche. La bibliothèque est particulièrement utile lorsque vous devez suivre de nombreuses parties mobiles, dont certaines se trouvent à différents états de cycle de vie. La bibliothèque inverse le fonctionnement des cycles de vie : habituellement, l'activité ou le fragment indique à un composant (tel que DessertTimer) ce qu'il doit faire 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 Lifecycle se compose de trois parties principales :
- Les propriétaires du cycle de vie, qui sont les composants qui ont (et donc "possèdent") un cycle de vie.
ActivityetFragmentsont des propriétaires de cycle de vie. Les propriétaires du cycle de vie implémentent l'interfaceLifecycleOwner. - 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. - Les observateurs du cycle de vie, qui observent l'état du cycle de vie et effectuent des tâches lorsque le cycle de vie change. Les observateurs de cycle de vie implémentent l'interface
LifecycleObserver.
Dans cette tâche, vous allez convertir l'application DessertClicker pour qu'elle utilise la bibliothèque de cycle de vie Android. Vous apprendrez également comment cette bibliothèque facilite la gestion des cycles de vie des activités et des fragments Android.
Étape 1 : Transformez DessertTimer en LifecycleObserver
La bibliothèque de cycle de vie repose en grande partie sur le concept d'observation du cycle de vie. L'observation permet aux classes (telles que DessertTimer) de connaître le cycle de vie de l'activité ou du fragment, et de démarrer et s'arrêter en réponse aux changements d'état de ces cycles de vie. Avec un observateur de cycle de vie, vous pouvez supprimer la responsabilité du démarrage et de l'arrêt des objets des méthodes d'activité et de fragment.
- Ouvrez la classe
DesertTimer.kt. - Modifiez la signature de la classe
DessertTimerpour qu'elle se présente comme suit :
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {Cette nouvelle définition de classe a deux objectifs :
- Le constructeur utilise un objet
Lifecycle, qui correspond au cycle de vie observé par le minuteur. - La définition de la classe implémente l'interface
LifecycleObserver.
- Sous la variable
runnable, ajoutez un blocinità la définition de la classe. Dans le blocinit, utilisez la méthodeaddObserver()pour connecter l'objet de cycle de vie transmis par le propriétaire (l'activité) à cette classe (l'observateur).
init {
lifecycle.addObserver(this)
}- Annotez
startTimer()avec@OnLifecycleEvent annotationet utilisez l'événement de cycle de vieON_START. Tous les événements de cycle de vie que votre observateur de cycle de vie peut observer se trouvent dans la classeLifecycle.Event.
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {- Faites de même pour
stopTimer(), en utilisant l'événementON_STOP:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()Étape 2 : Modifier MainActivity
Votre classe MainActivity est déjà un propriétaire du cycle de vie par héritage, car la superclasse FragmentActivity implémente LifecycleOwner. Par conséquent, vous n'avez rien à faire pour rendre votre activité sensible au cycle de vie. Il vous suffit de transmettre l'objet de cycle de vie de l'activité au constructeur DessertTimer.
- Ouvrez
MainActivity. Dans la méthodeonCreate(), modifiez l'initialisation deDessertTimerpour inclurethis.lifecycle:
dessertTimer = DessertTimer(this.lifecycle)La propriété lifecycle de l'activité contient l'objet Lifecycle que possède cette activité.
- Supprimez l'appel à
startTimer()dansonCreate()et l'appel àstopTimer()dansonStop(). Vous n'avez plus besoin d'indiquer àDessertTimerce qu'il doit faire dans l'activité, carDessertTimerobserve désormais le cycle de vie lui-même et est automatiquement averti lorsque l'état du cycle de vie change. Dans ces rappels, vous ne faites plus que consigner un message. - Compilez et exécutez l'application, puis ouvrez Logcat. Notez que le minuteur a démarré, comme prévu.

- Cliquez sur le bouton d'accueil pour mettre l'application en arrière-plan. Notez que le minuteur a cessé de fonctionner, comme prévu.
Qu'advient-il de votre application et de ses données si Android l'arrête alors qu'elle est en arrière-plan ? Il est important de comprendre ce cas limite délicat.
Lorsque votre application passe en arrière-plan, elle n'est pas détruite. Elle est simplement arrêtée et attend que l'utilisateur y retourne. Toutefois, l'une des principales préoccupations de l'OS Android est de faire en sorte que l'activité au premier plan s'exécute de manière fluide. Par exemple, si votre utilisateur utilise une application GPS pour l'aider à prendre un bus, il est important d'afficher rapidement cette application GPS et de continuer à afficher les instructions. Il est moins important de faire en sorte que l'application DessertClicker, que l'utilisateur n'a peut-être pas consultée depuis quelques jours, s'exécute correctement en arrière-plan.
Android régule les applications en arrière-plan pour que l'application au premier plan puisse fonctionner sans problème. Par exemple, Android limite le nombre d'opérations de traitement que les applications exécutées en arrière-plan peuvent effectuer.
Il arrive qu'Android arrête un processus d'application entier, ce qui inclut toutes les activités associées à l'application. Android effectue ce type d'arrêt lorsque le système est soumis à un stress et qu'un retard d'affichage risque de se produire. Par conséquent, aucun rappel ni code supplémentaire n'est exécuté à ce stade. Le processus de votre application est simplement arrêté en arrière-plan. Toutefois, aux yeux de l'utilisateur, il ne semble pas que l'application ait été fermée. Lorsque l'utilisateur revient à une application que le système Android a arrêtée, Android la redémarre.
Dans cette tâche, vous allez simuler l'arrêt d'un processus Android et examiner ce qui se passe lorsque votre application redémarre.
É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. Dans cette étape, vous allez utiliser adb pour fermer le processus de votre application et voir ce qui se passe lorsque Android arrête votre application.
- Compilez et exécutez votre application. Cliquez plusieurs fois sur le cupcake.
- Appuyez sur le bouton d'accueil pour mettre votre application en arrière-plan. Votre application est maintenant arrêtée et peut être fermée si Android a besoin des ressources qu'elle utilise.
- Dans Android Studio, cliquez sur l'onglet Terminal pour ouvrir le terminal de ligne de commande.

- Saisissez
adbet appuyez sur Retour.
Si vous voyez de nombreux résultats commençant parAndroid Debug Bridge version X.XX.Xet se terminant partags to be used by logcat (see logcat —help), tout va bien. Siadb: command not founds'affiche, assurez-vous que la commandeadbest disponible dans votre chemin d'exécution. Pour obtenir des instructions, consultez "Ajouter adb à votre chemin d'exécution" dans le chapitre sur les utilitaires. - Copiez et collez ce commentaire dans la ligne de commande, puis appuyez sur Retour :
adb shell am kill com.example.android.dessertclickerCette 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 en arrière-plan. Comme votre application était en arrière-plan, rien ne s'affiche sur l'écran de l'appareil ou de l'émulateur pour indiquer que votre processus a été arrêté. Dans Android Studio, cliquez sur l'onglet Run (Exécuter) pour afficher le message "Application terminated" (Application arrêtée). Cliquez sur l'onglet Logcat pour voir que le rappel onDestroy() n'a jamais été exécuté. Votre activité s'est simplement terminée.
- Utilisez l'écran "Actions récentes" pour revenir à l'application. Votre application apparaît dans les actions récentes, qu'elle ait été mise en arrière-plan ou complètement arrêtée. Lorsque vous utilisez l'écran des actions récentes pour revenir à l'application, l'activité est redémarrée. L'activité passe par l'ensemble des rappels de cycle de vie de démarrage, y compris
onCreate(). - Notez que lorsque l'application a redémarré, elle a réinitialisé votre "score" (le nombre de desserts vendus et le total des revenus) sur les valeurs par défaut (0). Si Android a arrêté votre application, pourquoi n'a-t-il pas enregistré son état ?
Lorsque l'OS redémarre votre application pour vous, Android fait de son mieux pour la réinitialiser à l'état dans lequel elle se trouvait auparavant. Android enregistre l'état de certaines de vos vues dans un bundle chaque fois que vous quittez l'activité. Voici quelques exemples de données qui sont automatiquement enregistrées : le texte d'un EditText (à condition qu'un ID soit défini dans la mise en page) et la pile de retour de votre activité.
Toutefois, il arrive que le système d'exploitation Android ne connaisse pas toutes vos données. Par exemple, si vous avez une variable personnalisée commerevenuedans l'application DessertClicker, l'OS Android ne connaît pas ces données ni leur importance pour votre activité. Vous devez ajouter vous-même ces données au bundle.
Étape 2 : Enregistrer les données du bundle à l'aide de la fonction onSaveInstanceState()
La méthode onSaveInstanceState() est le rappel qui permet d'enregistrer les données dont vous pourriez avoir besoin si l'OS Android détruisait votre application. Dans le schéma de 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 donne l'occasion d'enregistrer un petit volume d'informations dans un bundle lorsque votre activité quitte le premier plan. Le système enregistre ces données à ce stade, car s'il attendait que votre application s'arrête, l'OS pourrait manquer de ressources. En enregistrant les données à chaque fois, vous garantissez que les données mises à jour dans le bundle peuvent être restaurées, si nécessaire.
- Dans
MainActivity, remplacez le rappelonSaveInstanceState()et ajoutez une instruction de journalisationTimber.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Timber.i("onSaveInstanceState Called")
}- 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èsonPause()etonStop():
- En haut du fichier, juste avant la définition de 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 bundle d'états d'instance.
- Faites défiler la page jusqu'à
onSaveInstanceState()et notez le paramètreoutState, qui est de typeBundle.
Un bundle est un ensemble de paires clé-valeur, où les clés correspondent toujours à des chaînes. Vous pouvez placer des valeurs primitives, telles que des valeursintetboolean, dans le bundle.
Comme le système conserve ce bundle dans la RAM, il est recommandé de limiter la quantité de données qu'il contient. La taille de ce bundle est également limitée, même si elle varie d'un appareil à l'autre. En général, vous devez stocker beaucoup moins de 100 000 éléments, sinon vous risquez de faire planter votre application et de générer l'erreurTransactionTooLargeException. - Dans
onSaveInstanceState(), placez la valeurrevenue(un entier) dans le bundle avec la méthodeputInt():
outState.putInt(KEY_REVENUE, revenue)La méthode putInt() (et les méthodes similaires de la classe Bundle comme putFloat() et putString()) utilise deux arguments : une chaîne pour la clé (la constante KEY_REVENUE) et la valeur réelle à enregistrer.
- Répétez 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 : Restaurer les données d'un bundle à l'aide de la méthode onCreate()
- Faites défiler la page jusqu'à
onCreate()et examinez la signature de la méthode :
override fun onCreate(savedInstanceState: Bundle) {Notez qu'onCreate() reçoit un Bundle à chaque appel. Lorsque votre activité redémarre après l'arrêt d'un processus, le bundle que vous avez enregistré est transmis à onCreate(). Si l'activité recommençait à zéro, ce bundle dans onCreate() serait null. Si le bundle n'est pas null, vous savez que vous "recréez" l'activité à partir d'un point précédent connu.
- Ajoutez ce code à
onCreate(), après la configuration deDessertTimer:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}Le test pour null détermine si le bundle contient des données ou s'il est null, ce qui vous indique si l'application a été démarrée de zéro ou a été recréée après un arrêt. Ce test est un modèle courant de restauration des données 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'employer la même clé à chaque fois, il est recommandé de définir ces clés comme constantes. Utilisez getInt() pour extraire des données du bundle, tout comme vous avez utilisé putInt() pour y placer des données. La méthode getInt() comporte deux arguments :
- Une chaîne servant de clé, par exemple
"key_revenue"pour la valeur des revenus. - Une valeur par défaut si aucune valeur n'existe pour cette clé dans le bundle
L'entier que vous obtenez du bundle est ensuite attribué à la variable revenue. L'UI utilise cette valeur.
- Ajoutez des 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)
}- Compilez et exécutez l'application. Appuyez sur le cupcake au moins cinq fois jusqu'à obtenir un beignet. Cliquez sur Accueil pour mettre l'application en arrière-plan.
- Dans l'onglet Terminal d'Android Studio, exécutez
adbpour arrêter le processus de l'application.
adb shell am kill com.example.android.dessertclicker- Utilisez l'écran des actions récentes pour revenir à l'application. Notez que cette fois, l'application revient avec les valeurs de bundle correctes pour les revenus et les desserts vendus. Notez aussi que le dessert est à nouveau un cupcake. Il vous reste une dernière chose à faire pour vous assurer que l'application revient exactement là où elle s'était arrêtée.
- Dans
MainActivity, examinez la méthodeshowCurrentDessert(). 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 dans la variableallDesserts.
for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}Cette méthode s'appuie sur le nombre de desserts vendus pour choisir la bonne image. Vous n'avez donc rien à faire pour référencer une image dans le bundle dans onSaveInstanceState(). Dans ce bundle, vous stockez déjà le nombre de desserts vendus.
- Dans
onCreate(), dans le bloc qui restaure l'état du bundle, appelezshowCurrentDessert():
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()
}- Compilez et exécutez l'application, puis mettez-la en arrière-plan. Utilisez
adbpour arrêter le processus. Utilisez l'écran "Actions récentes" pour revenir à l'application. Notez que les valeurs des desserts vendus, le total des revenus et l'image du dessert sont correctement restaurés.
Il est important de connaître un dernier cas de figure dans la gestion du cycle de vie d'une activité et d'un fragment : l'impact des modifications de la 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 si radicale que le moyen le plus simple de s'adapter à cette modification consiste à arrêter complètement l'activité et à la recréer. Par exemple, si l'utilisateur change la langue de l'appareil, la mise en page devra peut-être changer pour s'adapter aux différentes orientations de texte. Si l'utilisateur branche l'appareil sur une station d'accueil ou ajoute un clavier physique, l'application devra peut-être s'ajuster à un changement de taille d'écran ou de mise en page. Enfin, si l'orientation de l'appareil change (s'il passe du mode portrait au mode paysage ou inversement), la mise en page peut avoir besoin d'être modifiée pour s'adapter à la nouvelle orientation.
Étape 1 : Explorez la rotation de l'appareil et les rappels de cycle de vie
- Compilez et exécutez votre application, puis ouvrez Logcat.
- Faites pivoter l'appareil ou l'émulateur en mode Paysage. Vous pouvez faire pivoter l'émulateur vers la gauche ou vers la droite à l'aide des boutons de rotation, ou avec
Controlet les touches fléchées (Commandet les touches fléchées sur Mac).
- Examinez la sortie dans Logcat. Filtrez la sortie 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 afin d'arrêter l'activité. Ensuite, lorsque l'activité est recréée, le système appelle tous les rappels de cycle de vie pour la démarrer. - Dans
MainActivity, mettez en commentaire l'intégralité de la méthodeonSaveInstanceState(). - Compilez et exécutez à nouveau votre application. Cliquez plusieurs fois sur le cupcake, puis faites pivoter l'appareil ou l'émulateur. Cette fois, lorsque l'appareil est pivoté et que l'activité est arrêtée et recréée, celle-ci démarre avec des valeurs par défaut.
Lorsqu'une modification de la configuration se produit, Android utilise le même bundle d'état d'instance que celui que vous avez découvert dans la tâche précédente pour enregistrer et restaurer l'état de l'application. Comme pour l'arrêt d'un processus, utilisezonSaveInstanceState()pour placer les données de votre application dans le bundle. Restaurez ensuite les données dansonCreate()pour éviter de perdre les données d'état de l'activité si l'appareil est pivoté. - Dans
MainActivity, supprimez le commentaire de la méthodeonSaveInstanceState(), exécutez l'application, cliquez sur le cupcake, puis faites pivoter l'application ou l'appareil. Notez que cette fois, les données sur les desserts sont conservées lors de la rotation de l'activité.
Projet Android Studio : DessertClickerFinal
Conseils sur le cycle de vie
- Si vous configurez ou démarrez quelque chose dans un rappel de cycle de vie, arrêtez ou supprimez cet élément dans le rappel correspondant. En arrêtant l'objet, vous vous assurez qu'il ne continue pas de fonctionner lorsqu'il n'est plus nécessaire. Par exemple, si vous configurez un minuteur dans
onStart(), vous devez le mettre en pause ou l'arrêter dansonStop(). - Utilisez
onCreate()uniquement pour initialiser les parties de votre application qui s'exécutent une seule fois, au premier démarrage de l'application. UtilisezonStart()pour démarrer les parties de votre application qui s'exécutent au démarrage de l'application et chaque fois qu'elle revient au premier plan.
Bibliothèque de cycle de vie
- Utilisez la bibliothèque de cycle de vie Android pour transférer le contrôle du cycle de vie de l'activité ou du fragment vers le composant réel qui doit être sensible au cycle de vie.
- Les propriétaires du cycle de vie sont des composants qui possèdent (et donc "détiennent") des cycles de vie, y compris
ActivityetFragment. Les propriétaires du cycle de vie implémentent l'interfaceLifecycleOwner. - Les observateurs du cycle de vie sont attentifs à l'état actuel du cycle de vie et effectuent des tâches lorsque le cycle de vie change. Les observateurs de cycle de vie implémentent l'interface
LifecycleObserver. - Les objets
Lifecyclecontiennent les états de cycle de vie réels et déclenchent des événements lorsque le cycle de vie change.
Pour créer une classe tenant compte du cycle de vie :
- Implémentez l'interface
LifecycleObserverdans 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 de cycle de vie, annotez les méthodes tenant compte du cycle de vie avec le changement d'état du cycle de vie 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 vieonStart.
Arrêt des processus et enregistrement de l'état de l'activité
- Android régule les applications qui s'exécutent en arrière-plan pour que l'application au premier plan puisse fonctionner sans problème. Cette réglementation inclut la limitation du nombre d'opérations de traitement que les applications en arrière-plan peuvent effectuer, et parfois même l'arrêt de l'ensemble du processus de votre application.
- L'utilisateur ne peut pas savoir si le système a arrêté une application en arrière-plan. L'application apparaît toujours dans l'écran "Récents" et doit redémarrer dans l'état dans lequel l'utilisateur l'a quitté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 utiliseradbpour simuler l'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'application s'arrête simplement.
Conserver l'état des activités et des fragments
- Lorsque votre application passe en arrière-plan, juste après l'appel de la méthode
onStop(), les données de l'application sont enregistrées dans un bundle. Certaines données de l'application, telles que le contenu d'un élémentEditText, sont automatiquement enregistrées. - Le bundle est une instance de
Bundle, qui correspond à un ensemble 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 bundle que vous souhaitez conserver, même si l'application a été fermée automatiquement. Pour placer des données dans le bundle, utilisez les méthodes correspondantes commençant parput, telles queputInt(). - Vous pouvez récupérer des données du bundle dans la méthode
onRestoreInstanceState()ou, plus communément, dansonCreate(). La méthodeonCreate()comporte un paramètresavedInstanceStatequi détient le bundle. - Si la variable
savedInstanceStatecontientnull, l'activité a été lancée sans bundle d'état et il n'existe pas de données d'état à récupérer. - Pour récupérer des données du bundle avec une clé, utilisez les méthodes
Bundlecommençant parget, par exemplegetInt().
Modifications de la configuration
- Une modification de la configuration se produit lorsque l'état de l'appareil change de manière si radicale que le moyen le plus simple de s'adapter à cette modification consiste à arrêter l'activité et à la recréer.
- L'exemple le plus courant de modification de la configuration est lorsque l'utilisateur fait pivoter l'appareil du mode Portrait au mode Paysage, ou inversement. Une modification de la configuration peut également se produire lorsque la langue de l'appareil change ou qu'un clavier matériel est branché.
- Lorsqu'une modification de configuration se produit, Android appelle tous les rappels d'arrêt du cycle de vie de l'activité. Android redémarre ensuite toute l'activité, en exécutant tous les rappels de démarrage du cycle de vie.
- Quand 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 d'un processus, enregistrez l'état de votre application dans le bundle dans
onSaveInstanceState().
Cours Udacity :
Documentation pour les développeurs Android :
- Activités (guide de l'API)
Activity(documentation de référence de l'API)- Comprendre le cycle de vie d'une activité
- Gérer les cycles de vie à l'aide de composants tenant compte des cycles de vie
LifecycleOwnerLifecycleLifecycleObserveronSaveInstanceState()- Gérer les modifications de configuration
- Enregistrer des états d'interface utilisateur
Autre :
- Timber (GitHub)
Cette section répertorie les devoirs possibles pour les élèves qui suivent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. Il revient à l'enseignant d'effectuer les opérations suivantes :
- Attribuer des devoirs si nécessaire
- Indiquer aux élèves comment rendre leurs devoirs
- Noter les devoirs
Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ne doivent pas hésiter à attribuer d'autres devoirs aux élèves s'ils le jugent nécessaire.
Si vous suivez cet atelier de programmation par vous-même, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.
Modifier une application
Ouvrez l'application DiceRoller de la leçon 1. (Si vous ne l'avez pas encore, vous pouvez télécharger l'application ici.) Compilez et exécutez l'application. Notez que si vous faites pivoter l'appareil, la valeur actuelle du dé est perdue. Implémentez onSaveInstanceState() pour conserver cette valeur dans le bundle et restaurez-la dans onCreate().
Répondre aux questions suivantes
Question 1
Votre application contient une simulation physique dont l'affichage nécessite des calculs complexes. L'utilisateur reçoit 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.
- Pendant l'appel téléphonique, vous devez arrêter de calculer la position des objets dans la simulation physique.
Question 2
Quelle méthode de cycle de vie devez-vous ignorer pour suspendre la simulation lorsque l'application ne s'affiche pas à l'écran ?
onDestroy()onStop()onPause()onSaveInstanceState()
Question 3
Pour qu'une classe tienne compte du cycle de vie via la bibliothèque de cycle de vie Android, quelle interface doit-elle implémenter ?
LifecycleLifecycleOwnerLifecycle.EventLifecycleObserver
Question 4
Dans quelles circonstances la méthode onCreate() de votre activité reçoit-elle un Bundle contenant des données (Bundle différent de null) ? Plusieurs réponses peuvent s'appliquer.
- L'activité est redémarrée après la rotation de l'appareil.
- L'activité est lancée à partir de zéro.
- L'activité reprend à son retour de l'arrière-plan.
- Redémarrage de l'appareil.
Passez à la leçon suivante :
Pour obtenir des liens vers d'autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.