1. Bienvenue
Cet atelier de programmation fait partie du module 1: Premiers pas avec le 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 plus d'informations sur le cours, y compris des liens vers tous les chapitres concernant les concepts, les applications et les diapositives, consultez Principes de base du développement Android (version 2).
Présentation
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.
- Ajoutez des instructions de journalisation à votre application et affichez ces journaux dans le volet "Logcat".
- Comprendre et travailler avec une activité et un intent, et être à l'aise pour interagir avec eux.
Ce que vous allez apprendre
- Fonctionnement du cycle de vie d'une activité
- Lorsqu'une activité démarre, s'interrompt, s'arrête et est détruite
- À propos des méthodes de rappel de cycle de vie associées aux modifications d'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 d'une 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.
- Observez 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 ajouter à l'application TwoActivities. L'application se comporte et se comporte à peu près comme dans le dernier atelier de programmation. Elle contient deux implémentations d'activité 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 du 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.
- Utilisez 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 copiez et collez onStart() 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 consigner.
- 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 indiquant 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, faites de même 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 à partir de MainActivity.)
- Ajoutez une instruction de journalisation à la méthode returnReply() juste avant la méthodefinish() :
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 Activity 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 revenir de la deuxième activité à l'activité principale.
- Utilisez la flèche vers le haut dans la barre d'application pour passer de la deuxième activité à l'activité principale.
- Faites pivoter l'appareil sur l'activité principale et secondaire à différents moments 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 la croix).
- Revenez à l'écran d'accueil et redémarrez votre application.
CONSEIL: Si vous exécutez votre application dans un émulateur, vous pouvez simuler la rotation avec les touches Ctrl+F11 ou Ctrl+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 :
Identique à 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 sa 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 risquent de 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 d'instance Activity. Le système enregistre les informations d'état par défaut dans le groupe d'états d'instances juste avant l'arrêt de l'activité, puis transmet ce bundle à la nouvelle instance d'activité à 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, au cours de 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é revient à 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'a activée. Même si l'activité est recréée, l'intent est toujours présent et les données qu'il contient sont toujours utilisées chaque fois que la méthode onCreate() de la deuxième activité est appelée.
Par ailleurs, dans chaque activité, le texte que vous avez saisi dans les éléments EditText du message ou de la réponse est conservé, même en cas de 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 est les é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 la seconde activité.
Dans cette tâche, vous allez ajouter du code pour conserver l'état d'instance de ces deux éléments TextView à l'aide de la fonction onSaveInstanceState().
- Ouvrez MainActivity.
- Ajoutez cette implémentation squelette de onSaveInstanceState() à Activity, 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, placez cet état de visibilité dans 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'il y ait une réponse de la deuxième activité. 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 au groupe.
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 est transmis à l'état Bundle avec la clé "reply_text".
Vous n'enregistrez que l'état des éléments "View" (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 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 soit dans onCreate(), soit en implémentant le rappel onRestoreInstanceState(), qui est appelé après onStart() une fois l'activité créée.
La plupart du temps, le meilleur endroit pour restaurer l'état de l'activité est onCreate(), afin de s'assurer que l'interface utilisateur, y compris son é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 que les variables View ont été initialisées avec findViewById(), ajoutez un test pour vous assurer que savedInstanceState n'est pas nul.
// 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 que la méthode onCreate() est appelée 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 ultérieurs de 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 groupe à 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) {
}
S'il existe une clé "response_visible" dans le bundle d'état (et que isVisible a donc la valeur "true"), vous devez restaurer l'état.
- Dans le test isVisible, rendez l'en-tête visible.
mReplyHeadTextView.setVisibility(View.VISIBLE);
- Récupérez le message de réponse textuel du bundle avec la clé "reply_text", puis configurez l'élément TextView de la 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éer une application de liste de courses simple avec une activité principale pour la liste créée par l'utilisateur et une deuxième activité 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 deuxième activité qui contient une liste d'articles courants (fromage, riz, pommes, etc.). Utilisez des éléments Button pour afficher les éléments.
- Sélectionner un article renvoie l'utilisateur à l'activité principale et met à jour un TextView vide pour inclure l'élément 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 associées à cette activité.
- Lorsque l'utilisateur passe d'une activité à une autre, et lorsqu'il se trouve à l'intérieur et à l'extérieur de votre application, chaque activité change d'état 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 votre classe Activity.
- Les méthodes de cycle de vie sont onCreate(), onStart(), onPause(), onRestart(), onResume(), onStop(), 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 (Remplacer).
- Les modifications apportées à la 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 en cas de 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 dans le bundle et les extraire.
- Restaurez l'état de l'instance dans onCreate() (méthode privilégiée) ou onRestoreInstanceState(). Retour