Ces travaux pratiques sont basés sur le cours de base pour les développeurs Android fourni par Google, qui prépare les participants au test de certification Associate Android Developer. Pour tirer le meilleur parti de ce TP, il est recommandé de travailler successivement dans les codelabs.
Dans cette pratique, vous en apprendrez plus sur le cycle de vie d'une activité (activity lifecycle). Le cycle de vie correspond à l'ensemble des états qu'une activité peut être pendant toute sa durée de vie, de sa création à sa destruction et à la récupération de ses ressources par le système. Lorsqu'un utilisateur navigue entre les activités de votre application (ainsi que dans et hors de celle-ci), les activités passent d'un état à un autre au cours de leur cycle de vie.
Chaque étape du cycle de vie d'une activité à une méthode de rappel (callback) 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 définissant l'une des méthodes de rappel du cycle de vie dans vos classes d'activité (Activity
), vous pouvez modifier le comportement par défaut de l'activité en réponse à des actions de l'utilisateur ou du système.
L'état d'activité peut également changer en réponse aux changements de configuration du périphérique, par exemple lorsque l'utilisateur fait pivoter le périphérique de portrait en paysage. Lorsque ces modifications de configuration se produisent, l'activité est détruite et recréée dans son état par défaut et l'utilisateur peut perdre les informations qu'il a entrées dans l'activité. Pour éviter toute confusion chez vos utilisateurs, il est important de développer votre application afin d'éviter toute perte de données inattendue. Plus loin dans cette pratique, vous expérimentez des modifications de configuration et apprendrez à préserver l'état d'une activité en réponse aux modifications de configuration de l'appareil et à d'autres événements de cycle de vie d'activité.
Dans cette pratique, vous ajoutez des instructions de journalisation à l'application TwoActivities et observez les modifications du cycle de vie des activités lorsque vous utilisez l'application. Vous commencez ensuite à travailler avec ces modifications et à explorer la manière de gérer les entrées utilisateur dans ces conditions.
Vous devriez être familier avec:
Activity
) et une intention (Intent
), et soyez à l'aise pour interagir avec elles.Dans cette pratique, vous ajoutez à l'application TwoActivities. L'application ressemble et se comporte approximativement de la même manière que dans le dernier code. Il contient deux implémentations d'activité et donne à l'utilisateur la possibilité d'envoyer des messages entre elles. Les modifications que vous apportez à l'application de cette manière n'affectent pas le comportement visible de l'utilisateur.
Dans cette tâche, vous allez implémenter toutes les méthodes de rappel du cycle de vie d'Activité pour imprimer des messages dans logcat lorsque ces méthodes sont appelées. Ces messages de journal vous permettent de savoir quand le cycle de vie de l'activité change d'état et comment ces changements affectent votre application lors de son exécution.
Pour les tâches de cette pratique, vous modifierez le projet TwoActivities existant que vous avez construit dans la dernière pratique. Si vous préférez conserver le projet TwoActivities précédent, suivez les étapes de l'Appendix: Utilities pour en créer une copie.
onCreate()
, ajoutez les instructions de journal suivantes:Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
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 dans Android Studio. Une boîte de dialogue apparaît avec toutes les méthodes possibles que vous pouvez remplacer dans votre classe. Le choix d'une ou de plusieurs méthodes de rappel dans la liste insère un modèle complet pour ces méthodes, y compris l'appel requis de la superclasse.
onStart()
comme modèle pour implémenter les rappels du 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 superclasse et pour enregistrer la bonne méthode.
D/MainActivity: -------
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onResume
Maintenant que vous avez implémenté les méthodes de rappel du cycle de vie pour MainActivity
, procédez de la même manière pour SecondActivity
.
LOG_TAG
:private static final String LOG_TAG = SecondActivity.class.getSimpleName();
MainActivity
.)returnReply()
juste avant la méthode finish()
:Log.d(LOG_TAG, "End SecondActivity");
LOG_TAG
de chaque classe contient les mots MainActivity
ou SecondActivity
. Ce mot-clé permet de filtrer le journal uniquement pour les éléments qui vous intéressent.Faites des essais avec votre application et notez que les événements du cycle de vie se produisent en réponse à différentes actions. En particulier, essayez ces choses:
Les extraits de code suivants montrent le code de la solution pour la première tâche.
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);
// Journaliser le début de la méthode onCreate().
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialiser toutes les variables de vue.
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");
}
Les extraits de code suivants montrent le code ajouté dans SecondActivity
, mais pas la classe entière.
Au sommet 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.
En fonction des ressources système et du comportement des utilisateurs, chaque activité de votre application peut être détruite et reconstruite bien plus souvent que vous ne le pensez.
Vous avez peut-être remarqué ce comportement dans la dernière section lorsque vous avez fait pivoter le périphérique ou l'émulateur. La rotation du périphérique est un exemple de changement de configuration de périphérique. Bien que la rotation soit la plus courante, toutes les modifications de configuration entraînent la destruction et la recréation de l'activité en cours comme si elle était nouvelle. Si vous ne tenez pas compte de ce problème dans votre code, en cas de modification de la configuration, votre structure Activité peut revenir à son apparence et à ses valeurs initiales par défaut 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 d'activité. Le système enregistre les informations d'état par défaut dans Bundle d'état d'instance juste avant l'arrêt de l'activité et le transmet à la nouvelle instance d'activité à restaurer.
Pour ne pas perdre de données dans une activité lorsque celle-ci 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 dans l'état d'instance sont spécifiques à 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 d'activité est perdu et l'activité reprend son apparence par défaut. Si vous devez enregistrer les données utilisateur entre les sessions d'application, utilisez les préférences partagées ou une base de données. Vous en apprendrez plus sur ces deux choses plus tard.
Vous avez peut-être remarqué que la rotation de l'appareil n'affecte en rien l'état de la deuxième activité. Cela est dû au fait que la deuxième présentation (layout) et l'état de l'activité sont générés à partir de la présentation (layout) et de l'intention (Intent
) qui l'a activée. Même si l'activité est recréée, l'intention (Intent
) est toujours présente et les données de cette intention sont toujours utilisées chaque fois que la méthode onCreate()
de la deuxième activité est appelée.
En outre, vous remarquerez peut-être que dans chaque activité, le texte que vous avez saisi dans les éléments message ou réponse EditText
est conservé, même lorsque le périphérique est pivoté. Cela est dû au fait que les informations d'état de certains des éléments View
de votre présentation (layout) sont automatiquement enregistrées lors des modifications de configuration et que la valeur actuelle d'un EditText
est l'un de ces cas.
Ainsi, les seuls états d'activité qui vous intéressent sont les éléments TextView
pour l'en-tête de réponse et le texte de réponse dans l'activité principale. Les deux éléments TextView
sont invisibles par défaut. Ils n'apparaissent que lorsque vous envoyez un message à l'activité principale à partir de la deuxième activité.
Dans cette tâche, vous ajoutez du code pour préserver l'état d'instance de ces deux éléments TextView
à l'aide de onSaveInstanceState()
.
onSaveInstanceState()
à l'activité ou utilisez Code > Override Methods pour insérer une substitution de squelette.@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
Bundle
avec la méthode putBoolean()
et la clé "reply_visible"
. if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
}
Rappelez-vous que l'en-tête de réponse et le texte sont marqués comme étant 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 nous ne sommes intéressés que par cet état de visibilité: le texte réel de l'en-tête n'a pas besoin d'être enregistré, car ce texte ne change jamais.
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 est également visible. 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 dans le Bundle
d'état avec la clé "reply_text"
.
Vous ne sauvegardez que l'état des éléments de vue susceptibles de changer après la création de l'activité. Les autres éléments View
de votre application (EditText
, Button
) peuvent être recréés à tout moment à partir de la présentation par défaut.
Notez que le système enregistrera l'état de certains éléments de la vue, tels que le contenu de la propriété EditText
.
onCreate()
Une fois que vous avez enregistré l'état de l'instance d'activité, vous devez également le restaurer lorsque l'activité est recréée. Vous pouvez le faire dans onCreate()
ou en implémentant le rappel onRestoreInstanceState()
, appelé après onStart()
après la création de l'activité.
La plupart du temps, le meilleur endroit pour restaurer l'état d'activité est dans onCreate()
, afin de s'assurer que l'interface utilisateur (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 de permettre aux sous-classes de décider d'utiliser ou non votre implémentation par défaut.
onCreate()
, une fois les variables View
initialisées avec findViewById()
, ajoutez un test pour vous assurer que savedInstanceState
n'est pas null.// Initialiser toutes les variables de vue.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restaurer l'état.
if (savedInstanceState != null) {
}
Lorsque votre activité est créée, le système passe le Bundle
d'état à onCreate()
comme seul argument. La première fois que onCreate()
est appelé et que votre application démarre, le Bundle
est nul
. Il n'y a pas d'état existant au premier démarrage de votre application. Les appels suivants dans onCreate()
ont un Bundle
rempli avec les données que vous avez stockées dans onSaveInstanceState()
.
Bundle
avec la clé "reply_visible"
.if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
}
if (isVisible) {
}
S'il existe une clé reply_visible
dans le Bundle d'état (et que isVisible est donc vrai), vous devrez restaurer l'état.
isVisible
, rendez l'en-tête visible.mReplyHeadTextView.setVisibility(View.VISIBLE);
Bundle
avec la clé "reply_text"
et configurez la réponse TextView
pour afficher cette chaîne.mReplyTextView.setText(savedInstanceState.getString("reply_text"));
TextView
de la réponse visible également:mReplyTextView.setVisibility(View.VISIBLE);
Les extraits de code suivants montrent le code de la solution pour cette tâche.
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);
// Si le titre est visible, le message doit être sauvegardé.
// Sinon, nous utilisons toujours la mise en page par défaut.
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");
// Initialiser toutes les variables de vue.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restaurer l'état enregistré.
// Voir onSaveInstanceState() pour ce qui est enregistré.
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
// Affichez à la fois les vues d'en-tête et de message. Si
// isVisible est faux ou manquant dans le bundle, utilisez la
// disposition par défaut.
if (isVisible) {
mReplyHeadTextView.setVisibility(View.VISIBLE);
mReplyTextView.setText(savedInstanceState
.getString("reply_text"));
mReplyTextView.setVisibility(View.VISIBLE);
}
}
}
onCreate()
, onStart()
, onPause()
, onRestart()
, onResume()
, onStop()
, onDestroy()
.EditText
. Pour toutes les autres données, vous devez explicitement enregistrer ces données vous-même.onSaveInstanceState()
.Bundle
. Utilisez les méthodes Bundle
pour insérer et récupérer des données dans Bundle
.onCreate()
, qui est la méthode préférée, ou onRestoreInstanceState()
.Documentation d'Android Studio:
Documentation développeur Android:
TextView
, un bouton pour incrémenter le compteur et un EditText
. Voir la capture d'écran ci-dessous à titre d'exemple. Vous n'avez pas à dupliquer précisément la mise en page (layout).EditText
.EditText
.onSaveInstanceState()
pour enregistrer l'état actuel de l'application.onCreate()
pour restaurer l'état de l'application.Si vous exécutez l'application de travail à faire avant d'implémenter onSaveInstanceState()
, que se passe-t-il si vous faites pivoter le périphérique? Choisissez-en un:
EditText
ne contient plus le texte que vous avez entré, mais le compteur est conservé.EditText
ne contient plus le texte que vous avez entré.EditText
est préservé.EditText
sont préservés.Quelles méthodes de cycle de vie d'activité sont appelées lorsqu'un changement de configuration de périphérique (tel qu'une rotation) se produit? Choisissez-en un:
onStop()
. Votre code doit redémarrer l'activité.onPause()
, onStop()
et onDestroy()
. Votre code doit redémarrer l'activité.onPause()
, onStop()
et onDestroy()
, puis redémarre l'opération en appelant onCreate()
, onStart()
et onResume()
.onResume()
.Lorsque dans le cycle de vie de l'activité, onSaveInstanceState()
est appelé? Choisissez-en un:
onSaveInstanceState()
est appelée avant la méthode onStop()
.onSaveInstanceState()
est appelée avant la méthode onResume()
.onSaveInstanceState()
est appelée avant la méthode onCreate()
.onSaveInstanceState()
est appelée avant la méthode onDestroy()
.Quelles méthodes de cycle de vie d'Activité sont les meilleures à utiliser pour enregistrer des données avant la fin ou la destruction de l'activité? Choisissez-en un:
onPause()
ou onStop()
onResume()
ou onCreate()
onDestroy()
onStart()
ou onRestart()
Vérifiez que l'application dispose des éléments suivants:
EditText
.EditText
sont conservés.MainActivity.java
utilise la méthode onSaveInstanceState()
pour stocker la valeur du compteur.onCreate()
teste l'existence du bundle
outState
. Si ce Bundle
existe, la valeur du compteur est restaurée et enregistrée dans TextView
.