1. Bienvenue
Cet atelier de programmation pratique fait partie du module 1: Premiers pas du cours Principes de base du développement Android (version 2). Vous tirerez le meilleur parti de ce cours en suivant les ateliers de programmation dans l'ordre:
- Pour obtenir la liste complète des ateliers de programmation du cours, consultez Ateliers de programmation pour le cours Principes de base du développement Android (V2).
- Pour en savoir plus sur le cours, y compris des liens vers tous les chapitres sur les concepts, les applications et les diapositives, consultez Principes de base du développement Android (version 2).
Introduction
Dans cet atelier, vous en apprendrez plus sur le cycle de vie d'une activité. Le cycle de vie correspond à l'ensemble des états dans lesquels une activité peut se trouver au cours de sa durée de vie, depuis sa création jusqu'à sa destruction et la récupération des ressources par le système. Lorsqu'un utilisateur passe d'une activité à une autre dans votre application (et quand il bascule de votre application à une autre), les activités changent d'état dans leur cycle de vie.
Chaque étape du cycle de vie d'une activité possède une méthode de rappel correspondante: onCreate(), onStart(), onPause(), etc. Lorsqu'une activité change d'état, la méthode de rappel associée est appelée. Vous avez déjà vu l'une de ces méthodes: onCreate(). En remplaçant l'une des méthodes de rappel de cycle de vie dans vos classes Activity, vous pouvez modifier le comportement par défaut de l'activité en réponse aux actions de l'utilisateur ou du système.
L'état de l'activité peut également évoluer en fonction des changements apportés à la configuration de l'appareil, par exemple lorsque l'utilisateur fait pivoter l'appareil en mode Paysage. Lorsque ces modifications de configuration se produisent, l'activité est détruite et recréée dans son état par défaut. L'utilisateur peut alors perdre les informations qu'il a saisies dans l'activité. Pour éviter de perturber vos utilisateurs, il est important de développer votre application afin d'éviter toute perte de données inattendue. Dans la suite de cet atelier, vous testerez des modifications de configuration et apprendrez à préserver l'état d'une activité en réponse à des changements apportés à la configuration de l'appareil et à d'autres événements de cycle de vie d'une activité.
Dans cet atelier, vous allez ajouter des instructions de journalisation dans l'application TwoActivities et observer les changements de cycle de vie de l'activité lorsque vous l'utilisez. Vous commencerez ensuite à apprivoiser ces modifications et découvrirez comment gérer les entrées utilisateur dans ces conditions.
Prérequis
Vous devez être en mesure d'effectuer les actions suivantes :
- Créez et exécutez un projet d'application dans Android Studio.
- Ajouter des instructions de journalisation à votre application et consulter ces journaux dans le volet Logcat
- Comprendre et utiliser une activité et un intent, et interagir facilement avec ces éléments
Ce que vous allez apprendre
- Fonctionnement du cycle de vie d'une activité
- Quand une activité démarre, se met en pause, s'arrête et est détruite
- Les méthodes de rappel de cycle de vie associées aux modifications d'une activité
- L'effet des actions (telles que les modifications de configuration) pouvant entraîner des événements de cycle de vie d'une activité
- Comment conserver l'état de l'activité lors des événements de cycle de vie
Objectifs de l'atelier
- Ajoutez du code à l'application TwoActivities de l'atelier précédent pour implémenter les différents rappels de cycle de vie d'une activité afin d'inclure des instructions de journalisation.
- Observer les changements d'état pendant l'exécution de votre application et lorsque vous interagissez avec chaque activité de votre application
- Modifier votre application pour conserver l'état d'instance d'une activité recréée de manière inattendue en réponse au comportement de l'utilisateur ou à un changement de configuration sur l'appareil
2. Présentation de l'application
Dans cet atelier, vous allez travailler avec l'application TwoActivities. L'application se comporte et fonctionne à peu près comme dans le dernier atelier de programmation. Elle contient deux implémentations d'activités et permet à l'utilisateur d'échanger entre elles. Les modifications que vous apportez à l'application dans cet atelier n'affecteront pas son comportement visible pour l'utilisateur.
3. 3. Tâche 1 : Ajouter des rappels de cycle de vie à TwoActivities
Dans cette tâche, vous allez implémenter toutes les méthodes de rappel de cycle de vie d'une activité pour imprimer des messages dans Logcat lorsque ces méthodes sont appelées. Ces messages de journal vous permettent de voir quand le cycle de vie d'une activité change d'état et comment ces changements affectent votre application pendant son exécution.
1.1 (Facultatif) Copier le projet TwoActivities
Pour les tâches de cet atelier, vous allez modifier le projet TwoActivities existant que vous avez créé dans le dernier atelier. Si vous préférez conserver le projet TwoActivities précédent intact, suivez la procédure décrite dans Annexe: Utilitaires pour créer une copie du projet.
1.2 Implémenter des rappels dans MainActivity
- Ouvrez le projet TwoActivities dans Android Studio, puis ouvrez MainActivity dans le volet Project > Android (Projet > Android).
- Dans la méthode onCreate(), ajoutez les instructions de journalisation suivantes:
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
- Ajoutez un remplacement pour le rappel onStart(), avec une instruction dans le journal pour cet événement:
@Override
public void onStart(){
super.onStart();
Log.d(LOG_TAG, "onStart");
}
Pour un raccourci, sélectionnez Code > Override Methods (Code > Remplacer les méthodes) dans Android Studio. Une boîte de dialogue s'affiche avec toutes les méthodes que vous pouvez remplacer dans votre classe. Si vous choisissez une ou plusieurs méthodes de rappel dans la liste, un modèle complet est inséré pour ces méthodes, y compris l'appel requis à la super-classe.
- Utiliser la méthode onStart() comme modèle pour implémenter les rappels de cycle de vie onPause(), onRestart(), onResume(), onStop() et onDestroy()
Toutes les méthodes de rappel ont les mêmes signatures (à l'exception du nom). Si vous utilisez les options Copie et Coller pour créer ces autres méthodes de rappel, n'oubliez pas de mettre à jour le contenu pour appeler la bonne méthode dans la super-classe et la journaliser.
- Exécutez votre application.
- Cliquez sur l'onglet Logcat en bas d'Android Studio pour afficher le volet Logcat. Vous devriez voir trois messages de journal affichant les trois états du cycle de vie par lesquels l'activité est passée au début:
D/MainActivity: -------
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onResume
1.3 Implémenter des rappels de cycle de vie dans SecondActivity
Maintenant que vous avez implémenté les méthodes de rappel de cycle de vie pour MainActivity, procédez de la même manière pour SecondActivity.
- Ouvrez SecondActivity.
- En haut de la classe, ajoutez une constante pour la variable LOG_TAG:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
- Ajoutez les rappels de cycle de vie et les instructions de journalisation à la deuxième activité. (Vous pouvez copier et coller les méthodes de rappel depuis MainActivity.)
- Ajoutez une instruction de journalisation à la méthode returnReply() juste avant la méthode finish() :
Log.d(LOG_TAG, "End SecondActivity");
1.4 Observer le journal pendant l'exécution de l'application**
- Exécutez votre application.
- Cliquez sur l'onglet Logcat en bas d'Android Studio pour afficher le volet Logcat.
- Saisissez "Activité" dans le champ de recherche. Android Logcat peut être très long et encombré. Étant donné que la variable LOG_TAG de chaque classe contient les mots MainActivity ou SecondActivity, ce mot clé vous permet de filtrer le journal pour n'afficher que ce qui vous intéresse.
Testez votre application et notez les événements de cycle de vie qui se produisent en réponse à différentes actions. Essayez plus particulièrement ces étapes :
- Utilisez l'application normalement (envoyez un message et répondez par un autre).
- Utilisez le bouton Retour pour passer de l'activité secondaire à l'activité principale.
- Utilisez la flèche vers le haut dans la barre d'application pour passer de l'activité secondaire à l'activité principale.
- Faites pivoter l'appareil sur les activités principale et secondaire à différents endroits de l'application et observez ce qui se passe dans le journal et à l'écran.
- Appuyez sur le bouton Aperçu (bouton carré à droite de l'écran d'accueil), puis fermez l'application (appuyez sur X).
- Revenez à l'écran d'accueil et redémarrez votre application.
ASTUCE: Si vous exécutez votre application dans un émulateur, vous pouvez simuler la rotation avec Ctrl+F11 ou Ctrl+Fonction+F11.
Code de solution de la tâche 1
Les extraits de code suivants montrent le code de solution de la première tâche.
MainActivity
Les extraits de code suivants montrent le code ajouté dans MainActivity, mais pas la classe entière.
La méthode onCreate() :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Log the start of the onCreate() method.
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
}
Les autres méthodes de cycle de vie :
@Override
protected void onStart() {
super.onStart();
Log.d(LOG_TAG, "onStart");
}
@Override
protected void onPause() {
super.onPause();
Log.d(LOG_TAG, "onPause");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LOG_TAG, "onRestart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(LOG_TAG, "onResume");
}
@Override
protected void onStop() {
super.onStop();
Log.d(LOG_TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "onDestroy");
}
SecondActivity
Les extraits de code suivants montrent le code ajouté dans SecondActivity, mais pas la classe entière.
En haut de la classe SecondActivity:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
La méthode returnReply() :
public void returnReply(View view) {
String reply = mReply.getText().toString();
Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_REPLY, reply);
setResult(RESULT_OK, replyIntent);
Log.d(LOG_TAG, "End SecondActivity");
finish();
}
Les autres méthodes de cycle de vie :
Identiques à MainActivity ci-dessus.
4. 4. Tâche 2 : Enregistrer et restaurer l'état de l'instance Activity
En fonction des ressources système et du comportement des utilisateurs, chaque activité de votre application peut être détruite et recréée bien plus fréquemment que vous ne le pensez.
Vous avez peut-être remarqué ce comportement dans la dernière section, lorsque vous avez fait pivoter l'appareil ou l'émulateur. La rotation de l'appareil est un exemple de modification de la configuration. Bien que la rotation soit la plus courante, toutes les modifications de configuration entraînent la destruction et la recréation de l'activité actuelle comme si elle était nouvelle. Si vous ne prenez pas en compte ce comportement dans votre code, en cas de modification de la configuration, la mise en page de votre activité peut revenir à son apparence par défaut et à ses valeurs initiales, et vos utilisateurs peuvent perdre leur place, leurs données ou l'état de leur progression dans votre application.
L'état de chaque activité est stocké sous la forme d'un ensemble de paires clé/valeur dans un objet Bundle appelé "état de l'instance d'activité". Le système enregistre les informations sur l'état par défaut dans le bundle d'état d'instance juste avant l'arrêt de l'activité, puis transmet ce bundle à la nouvelle instance d'activité pour la restaurer.
Pour éviter de perdre des données dans une activité lorsqu'elle est détruite et recréée de manière inattendue, vous devez implémenter la méthode onSaveInstanceState(). Le système appelle cette méthode sur votre activité (entre onPause() et onStop()) lorsqu'il est possible que l'activité soit détruite et recréée.
Les données que vous enregistrez à l'état d'instance ne concernent que cette instance de cette activité spécifique, pendant la session d'application en cours. Lorsque vous arrêtez et redémarrez une nouvelle session d'application, l'état de l'instance Activity est perdu et l'activité est rétablie à son apparence par défaut. Si vous devez enregistrer des données utilisateur entre les sessions d'application, utilisez des préférences partagées ou une base de données. Nous aborderons ces deux points ultérieurement dans un autre atelier.
2.1 Enregistrer l'état de l'instance Activity avec onSaveInstanceState()
Vous avez peut-être remarqué que la rotation de l'appareil n'affecte pas du tout l'état de la deuxième activité. En effet, la mise en page et l'état de la deuxième activité sont générés à partir de la mise en page et de l'intent qui l'ont activée. Même si l'activité est recréée, l'intent est toujours présent et les données de cet intent sont toujours utilisées chaque fois que la méthode onCreate() de la deuxième activité est appelée.
Vous pouvez également remarquer que, dans chaque activité, le texte que vous avez saisi dans les éléments EditText d'un message ou d'une réponse est conservé, même lors de la rotation de l'appareil. En effet, les informations d'état de certains éléments View de votre mise en page sont automatiquement enregistrées en cas de modification de la configuration, et la valeur actuelle d'un EditText en fait partie.
Ainsi, le seul état d'activité qui vous intéresse correspond aux éléments TextView pour l'en-tête de la réponse et le texte de la réponse dans l'activité principale. Les deux éléments TextView sont invisibles par défaut. Ils n'apparaissent que lorsque vous renvoyez un message à l'activité principale à partir de l'activité secondaire.
Dans cette tâche, vous allez ajouter du code pour conserver l'état d'instance de ces deux éléments TextView à l'aide de onSaveInstanceState().
- Ouvrez MainActivity.
- Ajoutez cette implémentation squelette de onSaveInstanceState() à l'activité, ou utilisez Code > Override Methods (Remplacer les méthodes) pour insérer un remplacement squelette.
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
- Vérifiez si l'en-tête est actuellement visible et, le cas échéant, définissez cet état de visibilité sur le bundle d'état à l'aide de la méthode putBoolean() et de la clé "reply_visible".
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
}
N'oubliez pas que l'en-tête et le texte de la réponse sont marqués comme invisibles jusqu'à ce qu'une réponse de la deuxième activité soit émise. Si l'en-tête est visible, des données de réponse doivent être enregistrées. Notez que seul l'état de visibilité nous intéresse. Il n'est pas nécessaire d'enregistrer le texte de l'en-tête, car il ne change jamais.
- Dans cette même vérification, ajoutez le texte de la réponse dans le bundle.
outState.putString("reply_text",mReplyTextView.getText().toString());
Si l'en-tête est visible, vous pouvez supposer que le message de réponse lui-même l'est également. Vous n'avez pas besoin de tester ni d'enregistrer l'état de visibilité actuel du message de réponse. Seul le texte du message passe à l'état "Bundle" avec la clé "reply_text".
Vous n'enregistrez que l'état des éléments de vue qui peuvent changer après la création de l'activité. Les autres éléments View de votre application (EditText et Button) peuvent être recréés à tout moment à partir de la mise en page par défaut.
Notez que le système enregistre l'état de certains éléments View, tels que le contenu de l'EditText.
2.2 Restaurer l'état de l'instance Activity dans onCreate()
Une fois que vous avez enregistré l'état de l'instance Activity, vous devez également le restaurer lors de la recréation de l'activité. Vous pouvez le faire dans onCreate() ou en implémentant le rappel onRestoreInstanceState(), qui est appelé après onStart() après la création de l'activité.
La plupart du temps, le meilleur endroit pour restaurer l'état de l'activité est dans onCreate(), afin de s'assurer que l'UI, y compris l'état, est disponible dès que possible. Il est parfois pratique de le faire dans onRestoreInstanceState() une fois l'initialisation terminée ou pour permettre aux sous-classes de décider d'utiliser ou non votre implémentation par défaut.
- Dans la méthode onCreate(), une fois les variables View initialisées avec findViewById(), ajoutez un test pour vous assurer que savedInstanceState n'est pas une valeur nulle.
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the state.
if (savedInstanceState != null) {
}
Lorsque votre activité est créée, le système transmet le bundle d'état à onCreate() comme seul argument. La première fois qu'onCreate() est appelé et que votre application démarre, le bundle est nul. Il n'existe aucun état existant la première fois que votre application démarre. Les appels suivants à onCreate() comportent un bundle contenant les données que vous avez stockées dans onSaveInstanceState().
- Dans cette vérification, récupérez la visibilité actuelle (true ou false) du bundle à l'aide de la clé "reply_visible".
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
}
- Ajoutez un test en dessous de cette ligne précédente pour la variable isVisible.
if (isVisible) {
}
Si une clé "reply_visible" existe dans le bundle d'état (et si "isVisible" est donc "true"), vous devrez restaurer l'état.
- Dans le test isVisible, rendez l'en-tête visible.
mReplyHeadTextView.setVisibility(View.VISIBLE);
- Obtenez le message de réponse textuel du bundle avec la clé "reply_text", puis définissez le TextView de réponse pour qu'il affiche cette chaîne.
mReplyTextView.setText(savedInstanceState.getString("reply_text"));
- Rendez également visible l'élément TextView de la réponse:
mReplyTextView.setVisibility(View.VISIBLE);
- Exécutez l'application. Essayez de faire pivoter l'appareil ou l'émulateur pour vous assurer que le message de réponse (le cas échéant) reste affiché à l'écran une fois l'activité recréée.
Code de solution de la tâche 2
Les extraits de code suivants montrent le code de solution de cette tâche.
MainActivity
Les extraits de code suivants montrent le code ajouté dans MainActivity, mais pas la classe entière.
La méthode onSaveInstanceState() :
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// If the heading is visible, message needs to be saved.
// Otherwise we're still using default layout.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
outState.putString("reply_text",
mReplyTextView.getText().toString());
}
}
La méthode onCreate() :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the saved state.
// See onSaveInstanceState() for what gets saved.
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
// Show both the header and the message views. If isVisible is
// false or missing from the bundle, use the default layout.
if (isVisible) {
mReplyHeadTextView.setVisibility(View.VISIBLE);
mReplyTextView.setText(savedInstanceState
.getString("reply_text"));
mReplyTextView.setVisibility(View.VISIBLE);
}
}
}
Le projet complet :
Projet Android Studio: TwoActivitiesLifecycle
5. Codage
Défi: Créez une application de liste de courses simple, avec une activité principale pour la liste créée par l'utilisateur et une activité secondaire pour une liste d'articles courants.
- L'activité principale doit contenir la liste à compiler, qui doit être composée de 10 éléments TextView vides.
- Un bouton "Ajouter un article" dans l'activité principale lance une activité secondaire, qui répertorie les articles courants (fromage, riz, pommes, etc.). Utilisez des éléments de bouton pour afficher les éléments.
- Si l'utilisateur sélectionne un article, il revient à l'activité principale. Un élément TextView vide est mis à jour pour inclure l'article choisi.
Utilisez un intent pour transmettre des informations d'une activité à une autre. Assurez-vous que l'état actuel de la liste de courses est enregistré lorsque l'utilisateur fait pivoter l'appareil.
6. Résumé
- Le cycle de vie d'une activité est un ensemble d'états par lesquels passe une activité, depuis sa création jusqu'à sa fin lorsque le système Android récupère les ressources allouées à cette activité.
- Lorsque l'utilisateur passe d'une activité à une autre, et de votre application à une autre, l'état change dans le cycle de vie de l'activité.
- Chaque état du cycle de vie de l'activité possède une méthode de rappel correspondante que vous pouvez remplacer dans la classe Activity.
- Les méthodes de cycle de vie sont les suivantes : onCreate(), onStart(), onPause(), onRestart(), onResume(), onStop() et onDestroy().
- Le remplacement d'une méthode de rappel de cycle de vie vous permet d'ajouter le comportement qui se produit lorsque votre activité passe à cet état.
- Vous pouvez ajouter des méthodes de remplacement squelettes à vos classes dans Android Studio via Code > Override (Code > Remplacer).
- Les modifications de configuration de l'appareil telles que la rotation entraînent la destruction et la recréation de l'activité, comme si elle était nouvelle.
- Une partie de l'état de l'activité est conservée lors d'une modification de la configuration, y compris les valeurs actuelles des éléments EditText. Pour toutes les autres données, vous devez les enregistrer vous-même de façon explicite.
- Enregistrez l'état de l'instance Activity dans la méthode onSaveInstanceState().
- Les données d'état de l'instance sont stockées sous forme de paires clé/valeur simples dans un bundle. Utilisez les méthodes Bundle pour insérer des données et les extraire du bundle.
- Restaurez l'état de l'instance dans onCreate() (méthode privilégiée) ou onRestoreInstanceState(). Retour