Cycle de vie d'une activité (Views)

Concepts et implémentation de Jetpack Compose

Lorsqu'un utilisateur navigue dans votre application, en sort et y revient, les instances Activity de votre application passent par différents états de leur cycle de vie. La classe Activity fournit un certain nombre de rappels qui permettent à l'activité de savoir quand un état change ou que le système crée, arrête ou reprend une activité, ou détruit le processus dans lequel l'activité réside.

Dans les méthodes de rappel du cycle de vie, vous pouvez déclarer le comportement de votre activité lorsque l'utilisateur la quitte et y revient. Par exemple, si vous créez un lecteur vidéo en streaming, vous pouvez mettre la vidéo en pause et mettre fin à la connexion réseau lorsque l'utilisateur passe à une autre application. Lorsque l'utilisateur revient, vous pouvez vous reconnecter au réseau et lui permettre de reprendre la vidéo au même endroit.

Chaque rappel vous permet d'effectuer un travail spécifique adapté à un changement d'état donné. Effectuer les bonnes tâches au bon moment et gérer correctement les transitions rendent votre application plus robuste et performante. Par exemple, une bonne implémentation des rappels de cycle de vie peut aider votre application à éviter les problèmes suivants :

  • Plantage si l'utilisateur reçoit un appel téléphonique ou passe à une autre application pendant qu'il utilise la vôtre.
  • Consommer de précieuses ressources système lorsque l'utilisateur ne l'utilise pas activement.
  • Perte de la progression de l'utilisateur s'il quitte votre application et y revient ultérieurement.
  • L'application plante ou la progression de l'utilisateur est perdue lorsque l'écran passe du mode paysage au mode portrait.

Ce document décrit en détail le cycle de vie d'une activité. Le document commence par décrire le paradigme du cycle de vie. Ensuite, il explique chacun des rappels : ce qui se passe en interne lors de leur exécution et ce que vous devez implémenter pendant leur exécution.

Il présente ensuite brièvement la relation entre l'état d'une activité et la vulnérabilité d'un processus à être arrêté par le système. Enfin, il aborde plusieurs sujets liés aux transitions entre les états d'activité.

Pour en savoir plus sur la gestion des cycles de vie, y compris sur les bonnes pratiques, consultez Gérer les cycles de vie avec des composants compatibles avec le cycle de vie et Enregistrer les états de l'UI. Pour découvrir comment concevoir une application robuste et de qualité à l'aide d'activités combinées à des composants d'architecture, consultez le Guide de l'architecture d'applications.

Concepts du cycle de vie d'une activité

Pour gérer les transitions entre les étapes du cycle de vie d'une activité, la classe Activity fournit un ensemble de six rappels : onCreate, onStart, onResume, onPause, onStop et onDestroy. Le système appelle chacun de ces rappels lorsque l'activité passe à un nouvel état.

La figure 1 présente une représentation visuelle de ce paradigme.

Figure 1. Illustration simplifiée du cycle de vie d'une activité.

Lorsque l'utilisateur commence à quitter l'activité, le système appelle des méthodes pour la démanteler. Dans certains cas, l'activité n'est que partiellement démontée et réside toujours en mémoire, par exemple lorsque l'utilisateur passe à une autre application. Dans ces cas, l'activité peut toujours revenir au premier plan.

Si l'utilisateur revient à l'activité, elle reprend là où il l'avait laissée. À quelques exceptions près, les applications ne sont pas autorisées à démarrer des activités lorsqu'elles s'exécutent en arrière-plan.

La probabilité que le système arrête un processus donné, ainsi que les activités qu'il contient, dépend de l'état de l'activité au moment de l'arrêt. Pour en savoir plus sur la relation entre l'état et la vulnérabilité à l'éjection, consultez la section sur l'état d'une activité et l'éjection de la mémoire.

En fonction de la complexité de votre activité, vous n'aurez probablement pas besoin d'implémenter toutes les méthodes de cycle de vie. Toutefois, il est important que vous compreniez chacun d'eux et que vous implémentiez ceux qui font que votre application se comporte comme les utilisateurs s'y attendent.

Rappels de cycle de vie

Cette section fournit des informations conceptuelles et d'implémentation sur les méthodes de rappel utilisées pendant le cycle de vie de l'activité.

Certaines actions appartiennent aux méthodes du cycle de vie de l'activité. Toutefois, placez le code qui implémente les actions d'un composant dépendant dans le composant, plutôt que dans la méthode de cycle de vie de l'activité. Pour ce faire, vous devez rendre le composant dépendant conscient du cycle de vie. Pour savoir comment rendre vos composants dépendants compatibles avec le cycle de vie, consultez Gérer les cycles de vie avec des composants compatibles avec le cycle de vie.

onCreate

Vous devez implémenter ce rappel, qui se déclenche lorsque le système crée l'activité pour la première fois. Lors de la création d'une activité, celle-ci passe à l'état Créé. Dans la méthode onCreate, exécutez la logique de démarrage de base de l'application qui ne se produit qu'une seule fois pour toute la durée de vie de l'activité.

Par exemple, votre implémentation de onCreate peut lier des données à des listes, associer l'activité à un ViewModel et instancier certaines variables de portée de classe. Cette méthode reçoit le paramètre savedInstanceState, qui est un objet Bundle contenant l'état de l'activité précédemment enregistré. Si l'activité n'a jamais existé auparavant, la valeur de l'objet Bundle est nulle.

Si vous avez un composant tenant compte du cycle de vie qui est associé au cycle de vie de votre activité, il reçoit l'événement ON_CREATE. La méthode annotée avec @OnLifecycleEvent est appelée pour que votre composant tenant compte du cycle de vie puisse exécuter le code de configuration dont il a besoin pour l'état créé.

L'exemple suivant de la méthode onCreate montre la configuration de base de l'activité, comme la déclaration de l'interface utilisateur (définie dans un fichier de mise en page XML), la définition des variables membres et la configuration de l'interface utilisateur. Dans cet exemple, le fichier de mise en page XML transmet l'ID de ressource du fichier R.layout.main_activity à setContentView.

Kotlin

lateinit var textView: TextView

// Some transient state for the activity instance.
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState)

    // Recover the instance state.
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity)

    // Initialize member TextView so it is available later.
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState)
}

Java

TextView textView;
// Some transient state for the activity instance.
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState);

    // Recover the instance state.
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity);

    // Initialize member TextView so it is available later.
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString(GAME_STATE_KEY, gameState);
    outState.putString(TEXT_VIEW_KEY, textView.getText());

    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState);
}

Au lieu de définir le fichier XML et de le transmettre à setContentView, vous pouvez créer des objets View dans le code de votre activité et créer une hiérarchie de vues en insérant de nouveaux objets View dans un ViewGroup. Vous utilisez ensuite cette mise en page en transmettant le ViewGroup racine à setContentView. Pour en savoir plus sur la création d'une interface utilisateur, consultez la documentation sur l'interface utilisateur.

Votre activité ne reste pas à l'état "Créée". Une fois la méthode onCreate exécutée, l'activité passe à l'état Démarré et le système appelle rapidement les méthodes onStart et onResume.

onStart

Lorsque l'activité passe à l'état "Démarré", le système appelle onStart. Cet appel rend l'activité visible pour l'utilisateur pendant que l'application se prépare à ce que l'activité passe au premier plan et devienne interactive. Par exemple, c'est dans cette méthode que le code qui gère l'UI est initialisé.

Lorsque l'activité passe à l'état "Démarré", tout composant tenant compte du cycle de vie associé au cycle de vie de l'activité reçoit l'événement ON_START.

La méthode onStart se termine rapidement et, comme pour l'état "Created", l'activité ne reste pas à l'état "Started". Une fois ce rappel terminé, l'activité passe à l'état Reprise et le système appelle la méthode onResume.

onResume

Lorsque l'activité passe à l'état "Reprise", elle passe au premier plan et le système appelle le rappel onResume. Il s'agit de l'état dans lequel l'application interagit avec l'utilisateur. L'application reste dans cet état jusqu'à ce qu'un événement lui fasse perdre le focus (par exemple, l'appareil reçoit un appel téléphonique, l'utilisateur accède à une autre activité ou l'écran de l'appareil s'éteint).

Lorsque l'activité passe à l'état "Reprise", tout composant tenant compte du cycle de vie associé au cycle de vie de l'activité reçoit l'événement ON_RESUME. C'est là que les composants du cycle de vie peuvent activer toute fonctionnalité qui doit s'exécuter lorsque le composant est visible et au premier plan, comme le démarrage d'un aperçu de caméra.

Lorsqu'un événement interruptif se produit, l'activité passe à l'état Paused (Mise en pause) et le système appelle le rappel onPause.

Si l'activité revient à l'état "Reprise" à partir de l'état "Pause", le système appelle à nouveau la méthode onResume. Pour cette raison, implémentez onResume pour initialiser les composants que vous libérez pendant onPause et pour effectuer toute autre initialisation qui doit avoir lieu chaque fois que l'activité passe à l'état "Reprise".

Voici un exemple de composant compatible avec le cycle de vie qui accède à la caméra lorsque le composant reçoit l'événement ON_RESUME :

Kotlin

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

Java

public class CameraComponent implements LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void initializeCamera() {
        if (camera == null) {
            getCamera();
        }
    }
    ...
}

Le code précédent initialise la caméra une fois que LifecycleObserver reçoit l'événement ON_RESUME. En mode multifenêtre, votre activité peut être entièrement visible même lorsqu'elle est en pause. Par exemple, lorsque l'application est en mode multifenêtre et que l'utilisateur appuie sur la fenêtre qui ne contient pas votre activité, celle-ci passe à l'état "En pause".

Si vous souhaitez que la caméra ne soit active que lorsque l'application est en mode "Reprise" (visible et active au premier plan), initialisez la caméra après l'événement ON_RESUME présenté précédemment. Si vous souhaitez que la caméra reste active lorsque l'activité est mise en pause, mais visible (par exemple, en mode multifenêtre), initialisez la caméra après l'événement ON_START.

Toutefois, si l'appareil photo est actif alors que votre activité est mise en veille, une autre application en mode multifenêtre peut se voir refuser l'accès à l'appareil photo. Il est parfois nécessaire de maintenir la caméra active lorsque votre activité est mise en pause, mais cela peut en fait dégrader l'expérience utilisateur globale.

Pour cette raison, réfléchissez bien à l'endroit du cycle de vie où il est le plus approprié de prendre le contrôle des ressources système partagées dans le contexte du mode multifenêtre. Pour en savoir plus sur la prise en charge du mode multifenêtre, consultez Prise en charge du mode multifenêtre.

Quel que soit l'événement de création que vous choisissez pour effectuer une opération d'initialisation, veillez à utiliser l'événement de cycle de vie correspondant pour libérer la ressource. Si vous initialisez un élément après l'événement ON_START, libérez-le ou mettez-y fin après l'événement ON_STOP. Si vous initialisez après l'événement ON_RESUME, libérez après l'événement ON_PAUSE.

L'extrait de code précédent place le code d'initialisation de la caméra dans un composant compatible avec le cycle de vie. Vous pouvez plutôt placer ce code directement dans les rappels de cycle de vie de l'activité, tels que onStart et onStop, mais nous ne le recommandons pas. L'ajout de cette logique à un composant indépendant et tenant compte du cycle de vie vous permet de réutiliser le composant dans plusieurs activités sans avoir à dupliquer le code. Pour savoir comment créer un composant tenant compte du cycle de vie, consultez Gérer les cycles de vie à l'aide de composants tenant compte des cycles de vie (Vues).

onPause

Le système appelle cette méthode comme première indication que l'utilisateur quitte votre activité, mais cela ne signifie pas toujours que l'activité est détruite. Cela indique que l'activité n'est plus au premier plan, mais qu'elle est toujours visible si l'utilisateur est en mode multifenêtre. Plusieurs raisons peuvent expliquer qu'une activité passe à cet état :

  • Un événement qui interrompt l'exécution de l'application, comme décrit dans la section sur le rappel onResume, met en pause l'activité en cours. Il s'agit du cas le plus courant.
  • En mode multifenêtre, une seule application est active à la fois, et le système met en pause toutes les autres applications.
  • L'ouverture d'une nouvelle activité semi-transparente, telle qu'une boîte de dialogue, met en pause l'activité qu'elle recouvre. Tant que l'activité est partiellement visible, mais pas ciblée, elle reste en pause.

Lorsqu'une activité passe à l'état "En pause", tout composant tenant compte du cycle de vie lié au cycle de vie de l'activité reçoit l'événement ON_PAUSE. C'est là que les composants du cycle de vie peuvent arrêter toute fonctionnalité qui n'a pas besoin de s'exécuter lorsque le composant n'est pas au premier plan, comme l'arrêt d'un aperçu de caméra.

Utilisez la méthode onPause pour suspendre ou ajuster les opérations qui ne peuvent pas continuer ou qui peuvent continuer avec modération lorsque le Activity est à l'état "En pause" et que vous prévoyez de reprendre sous peu.

Vous pouvez également utiliser la méthode onPause pour libérer les ressources système, les handles vers les capteurs (comme le GPS) ou toute ressource qui affecte l'autonomie de la batterie lorsque votre activité est mise en pause et que l'utilisateur n'en a pas besoin.

Toutefois, comme indiqué dans la section sur onResume, une activité mise en pause peut toujours être entièrement visible si l'application est en mode multifenêtre. Envisagez d'utiliser onStop au lieu de onPause pour libérer ou ajuster complètement les ressources et opérations liées à l'UI afin de mieux prendre en charge le mode multifenêtre.

L'exemple suivant de LifecycleObserver réagissant à l'événement ON_PAUSE est la contrepartie de l'exemple d'événement ON_RESUME précédent, qui libère la caméra qui s'initialise après la réception de l'événement ON_RESUME :

Kotlin

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

Java

public class JavaCameraComponent implements LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void releaseCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }
    ...
}

Cet exemple place le code de déclenchement de l'appareil photo après que l'événement ON_PAUSE a été reçu par LifecycleObserver.

L'exécution de onPause est très brève et ne laisse pas forcément assez de temps pour effectuer des opérations d'enregistrement. Pour cette raison, n'utilisez pas onPause pour enregistrer des données d'application ou des informations sur l'utilisateur, effectuer des appels réseau ou exécuter des transactions de base de données. Il est possible que ces opérations ne soient pas terminées avant la fin de la méthode.

Effectuez plutôt les opérations d'arrêt à forte charge pendant onStop. Pour en savoir plus sur les opérations appropriées à effectuer pendant onStop, consultez la section suivante. Pour en savoir plus sur l'enregistrement des données, consultez la section sur l'enregistrement et la restauration de l'état.

L'exécution de la méthode onPause ne signifie pas que l'activité quitte l'état "En pause". L'activité reste dans cet état jusqu'à ce qu'elle reprenne ou devienne complètement invisible pour l'utilisateur. Si l'activité reprend, le système appelle à nouveau le rappel onResume.

Si l'activité passe de l'état "En pause" à l'état "Reprise", le système conserve l'instance Activity en mémoire et la rappelle lorsqu'il appelle onResume. Dans ce cas, vous n'avez pas besoin de réinitialiser les composants créés lors de l'une des méthodes de rappel menant à l'état "Reprise". Si l'activité devient complètement invisible, le système appelle onStop.

onStop

Lorsque votre activité n'est plus visible par l'utilisateur, elle passe à l'état Arrêtée et le système appelle le rappel onStop. Cela peut se produire lorsqu'une activité nouvellement lancée couvre l'intégralité de l'écran. Le système appelle également onStop lorsque l'activité a terminé de s'exécuter et est sur le point d'être arrêtée.

Lorsque l'activité passe à l'état "Arrêtée", tout composant tenant compte du cycle de vie lié au cycle de vie de l'activité reçoit l'événement ON_STOP. C'est là que les composants du cycle de vie peuvent arrêter toute fonctionnalité qui n'a pas besoin de s'exécuter lorsque le composant n'est pas visible à l'écran.

Dans la méthode onStop, libérez ou ajustez les ressources qui ne sont pas nécessaires lorsque l'application n'est pas visible par l'utilisateur. Par exemple, votre application peut mettre en pause les animations ou passer de mises à jour de position précises à des mises à jour moins précises. L'utilisation de onStop au lieu de onPause signifie que le travail lié à l'UI se poursuit, même lorsque l'utilisateur affiche votre activité en mode multifenêtre.

Utilisez également onStop pour effectuer des opérations d'arrêt relativement gourmandes en ressources processeur. Par exemple, si vous ne trouvez pas de meilleur moment pour enregistrer des informations dans une base de données, vous pouvez le faire pendant onStop. L'exemple suivant montre une implémentation de onStop qui enregistre le contenu d'une note provisoire dans un espace de stockage persistant :

Kotlin

override fun onStop() {
    // Call the superclass method first.
    super.onStop()

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}

Java

@Override
protected void onStop() {
    // Call the superclass method first.
    super.onStop();

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate (
            mToken,  // int token to correlate calls
            null,    // cookie, not used here
            uri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
    );
}

L'exemple de code précédent utilise directement SQLite. Toutefois, nous vous recommandons d'utiliser Room, une bibliothèque de persistance qui fournit une couche d'abstraction sur SQLite. Pour en savoir plus sur les avantages de l'utilisation de Room et sur la façon d'implémenter Room dans votre application, consultez le guide Bibliothèque de persistance Room.

Lorsque votre activité passe à l'état "Arrêtée", l'objet Activity est conservé en mémoire : il conserve toutes les informations d'état et de membre, mais n'est pas associé au gestionnaire de fenêtres. Lorsque l'activité reprend, elle rappelle ces informations.

Vous n'avez pas besoin de réinitialiser les composants créés lors de l'une des méthodes de rappel menant à l'état "Reprise". Le système assure également le suivi de l'état actuel de chaque objet View dans la mise en page. Ainsi, si l'utilisateur saisit du texte dans un widget EditText, ce contenu est conservé et vous n'avez pas besoin de l'enregistrer ni de le restaurer.

À partir de l'état "Arrêtée", l'activité revient pour interagir avec l'utilisateur ou elle a terminé de s'exécuter et disparaît. Si l'activité revient au premier plan, le système appelle onRestart. Si l'exécution de Activity est terminée, le système appelle onDestroy.

onDestroy

onDestroy est appelé avant la destruction de l'activité. Le système appelle ce rappel pour l'une des deux raisons suivantes :

  1. L'activité se termine, car l'utilisateur l'a complètement ignorée ou parce que finish a été appelé sur l'activité.
  2. Le système détruit temporairement l'activité en raison d'une modification de la configuration, comme la rotation de l'appareil ou le passage en mode multifenêtre.

Lorsque l'activité passe à l'état détruit, tout composant tenant compte du cycle de vie lié au cycle de vie de l'activité reçoit l'événement ON_DESTROY. C'est là que les composants du cycle de vie peuvent nettoyer tout ce dont ils ont besoin avant que Activity ne soit détruit.

Au lieu de placer la logique dans votre Activity pour déterminer pourquoi elle est détruite, utilisez un objet ViewModel pour contenir les données de vue pertinentes pour votre Activity. Si Activity est recréé en raison d'une modification de la configuration, ViewModel n'a rien à faire, car il est conservé et transmis à la prochaine instance Activity.

Si Activity n'est pas recréé, la méthode onCleared est appelée pour ViewModel, ce qui lui permet de nettoyer toutes les données dont il a besoin avant d'être détruit. Vous pouvez faire la distinction entre ces deux scénarios à l'aide de la méthode isFinishing.

Si l'activité se termine, onDestroy est le dernier rappel de cycle de vie que l'activité reçoit. Si onDestroy est appelé à la suite d'une modification de la configuration, le système crée immédiatement une instance d'activité, puis appelle onCreate sur cette nouvelle instance dans la nouvelle configuration.

Le rappel onDestroy libère toutes les ressources qui n'ont pas été libérées par les rappels précédents, comme onStop.

Enregistrer et restaurer l'état transitoire de l'UI

Un utilisateur s'attend à ce que l'état de l'interface utilisateur d'une activité reste le même en cas de modification de la configuration, par exemple en cas de rotation ou d'un passage en mode multifenêtre. Toutefois, le système détruit par défaut l'activité lorsqu'une telle modification de configuration se produit, effaçant tout état d'interface utilisateur stocké dans l'instance d'activité.

De même, un utilisateur s'attend à ce que l'état de l'UI reste le même s'il passe temporairement de votre application à une autre, puis revient plus tard à la vôtre. Cependant, le système peut détruire le processus de votre application lorsque l'utilisateur est absent et que votre activité est arrêtée.

Lorsque des contraintes système détruisent l'activité, conservez l'état transitoire de l'interface utilisateur de l'utilisateur à l'aide d'une combinaison de ViewModel, onSaveInstanceState et/ou du stockage local. Pour en savoir plus sur les attentes des utilisateurs par rapport au comportement du système et sur la meilleure façon de préserver les données d'état de l'UI complexes en cas d'arrêt d'un processus ou d'une activité initié par le système, consultez Enregistrer les états de l'UI.

Cette section décrit l'état de l'instance et explique comment implémenter la méthode onSaveInstance, qui est un rappel sur l'activité elle-même. Si vos données d'UI sont légères, vous pouvez utiliser onSaveInstance seul pour conserver l'état de l'UI en cas de modifications de configuration et d'arrêt du processus initié par le système. Toutefois, comme onSaveInstance entraîne des coûts de sérialisation/désérialisation, dans la plupart des cas, vous utilisez à la fois ViewModel et onSaveInstance, comme indiqué dans Enregistrer les états de l'UI.

État de l'instance

Dans certains cas, votre activité est détruite en raison du comportement normal de l'application, par exemple lorsque l'utilisateur appuie sur le bouton Retour ou que votre activité signale sa propre destruction en appelant la méthode finish.

Lorsque votre activité est détruite parce que l'utilisateur appuie sur Retour ou que l'activité se termine d'elle-même, le concept d'instance Activity disparaît à jamais, à la fois pour le système et pour l'utilisateur. Dans ces scénarios, l'attente de l'utilisateur correspond au comportement du système, et vous n'avez aucun travail supplémentaire à effectuer.

Toutefois, si le système détruit l'activité en raison de contraintes système (telles qu'une modification de la configuration ou une pression sur la mémoire), même si l'instance Activity réelle a disparu, le système se souvient de son existence. Si l'utilisateur tente de revenir à l'activité, le système crée une instance de cette activité à l'aide d'un ensemble de données enregistrées qui décrivent l'état de l'activité lorsqu'elle a été détruite.

Les données enregistrées que le système utilise pour restaurer l'état précédent sont appelées état de l'instance. Il s'agit d'une collection de paires clé-valeur stockées dans un objet Bundle. Par défaut, le système utilise l'état de l'instance Bundle pour enregistrer des informations sur chaque objet View dans la mise en page de votre activité, comme la valeur du texte saisi dans un widget EditText.

Ainsi, si votre instance d'activité est détruite et recréée, l'état de la mise en page est restauré à son état précédent sans que vous ayez à écrire de code. Toutefois, votre activité peut contenir davantage d'informations sur l'état que vous souhaitez restaurer, comme des variables membres qui suivent la progression de l'utilisateur dans l'activité.

Un objet Bundle n'est pas adapté à la conservation d'une quantité de données non négligeable, car il nécessite une sérialisation sur le thread principal et consomme de la mémoire du processus système. Pour conserver une quantité de données plus importante, adoptez une approche combinée en utilisant le stockage local persistant, la méthode onSaveInstanceState et la classe ViewModel, comme indiqué dans Enregistrer les états de l'UI.

Enregistrer un état d'UI simple et léger à l'aide de onSaveInstanceState

Lorsque votre activité commence à s'arrêter, le système appelle la méthode onSaveInstanceState afin que votre activité puisse enregistrer les informations d'état dans un bundle d'état d'instance. L'implémentation par défaut de cette méthode enregistre des informations temporaires sur l'état de la hiérarchie de vues de l'activité, telles que le texte d'un widget EditText ou la position de défilement d'un widget ListView.

Pour enregistrer des informations supplémentaires sur l'état de l'instance pour votre activité, remplacez onSaveInstanceState et ajoutez des paires clé/valeur à l'objet Bundle qui est enregistré en cas de destruction inattendue de votre activité. Lorsque vous remplacez onSaveInstanceState, vous devez appeler l'implémentation de la super-classe si vous souhaitez que l'implémentation par défaut enregistre l'état de la hiérarchie des vues. Ce processus est illustré dans l'exemple suivant :

Kotlin

override fun onSaveInstanceState(outState: Bundle?) {
    // Save the user's current game state.
    outState?.run {
        putInt(STATE_SCORE, currentScore)
        putInt(STATE_LEVEL, currentLevel)
    }

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(outState)
}

companion object {
    val STATE_SCORE = "playerScore"
    val STATE_LEVEL = "playerLevel"
}

Java

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state.
    savedInstanceState.putInt(STATE_SCORE, currentScore);
    savedInstanceState.putInt(STATE_LEVEL, currentLevel);

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(savedInstanceState);
}

Pour enregistrer des données persistantes, telles que les préférences utilisateur ou les données d'une base de données, saisissez les occasions appropriées lorsque votre activité est au premier plan. Si aucune opportunité de ce type ne se présente, enregistrez les données persistantes pendant la méthode onStop.

Restaurer l'état de l'UI d'une activité à l'aide de l'état d'instance enregistré

Lorsque votre activité est recréée après avoir été détruite, vous pouvez récupérer l'état de l'instance enregistrée à partir de Bundle que le système transmet à votre activité. Les méthodes de rappel onCreate et onRestoreInstanceState reçoivent le même Bundle contenant les informations sur l'état de l'instance.

Étant donné que la méthode onCreate est appelée, que le système crée une instance de votre activité ou en recrée une précédente, vous devez vérifier si l'état Bundle est nul avant de tenter de le lire. Si elle est nulle, le système crée une instance de l'activité au lieu de restaurer une instance précédente qui a été détruite.

L'extrait de code suivant montre comment restaurer certaines données d'état dans onCreate :

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState) // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        with(savedInstanceState) {
            // Restore value of members from saved state.
            currentScore = getInt(STATE_SCORE)
            currentLevel = getInt(STATE_LEVEL)
        }
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        // Restore value of members from saved state.
        currentScore = savedInstanceState.getInt(STATE_SCORE);
        currentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

Au lieu de restaurer l'état pendant onCreate, vous pouvez choisir d'implémenter onRestoreInstanceState, que le système appelle après la méthode onStart. Le système appelle onRestoreInstanceState uniquement s'il existe un état enregistré à restaurer. Vous n'avez donc pas besoin de vérifier si Bundle est nul.

Kotlin

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState)

    // Restore state members from saved instance.
    savedInstanceState?.run {
        currentScore = getInt(STATE_SCORE)
        currentLevel = getInt(STATE_LEVEL)
    }
}

Java

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance.
    currentScore = savedInstanceState.getInt(STATE_SCORE);
    currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

Il est probable qu'une application entre dans une activité et en sorte, peut-être plusieurs fois, au cours de sa durée de vie, par exemple lorsque l'utilisateur appuie sur le bouton Retour de l'appareil ou lorsque l'activité lance une autre activité.

Cette section aborde les sujets que vous devez connaître pour implémenter des transitions d'activité efficaces. Ces thèmes incluent le démarrage d'une activité à partir d'une autre activité, l'enregistrement de l'état d'une activité et la restauration de l'état d'une activité.

Démarrer une activité à partir d'une autre

Une activité doit souvent en démarrer une autre à un moment donné. Ce besoin se présente, par exemple, lorsqu'une application doit passer de l'écran actuel à un nouvel écran.

Selon que votre activité souhaite ou non obtenir un résultat de la nouvelle activité qu'elle est sur le point de démarrer, vous démarrez la nouvelle activité à l'aide de la méthode startActivity ou de la méthode startActivityForResult. Dans les deux cas, vous transmettez un objet Intent.

L'objet Intent spécifie l'activité exacte que vous souhaitez démarrer ou décrit le type d'action que vous souhaitez effectuer. Le système sélectionne l'activité appropriée pour vous, qui peut même provenir d'une autre application. Un objet Intent peut également transporter de petites quantités de données à utiliser par l'activité qui est lancée. Pour en savoir plus sur la classe Intent, consultez Intents et filtres d'intent.

startActivity

Si l'activité nouvellement démarrée n'a pas besoin de renvoyer de résultat, l'activité actuelle peut la démarrer en appelant la méthode startActivity.

Lorsque vous travaillez dans votre propre application, vous devez souvent simplement lancer une activité connue. Par exemple, l'extrait de code suivant montre comment lancer une activité appelée SignInActivity.

Kotlin

val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)

Java

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

Votre application peut également effectuer une action, comme envoyer un e-mail, un message ou une mise à jour de l'état, à l'aide des données de votre activité. Dans ce cas, il est possible que votre application ne dispose pas de ses propres activités pour effectuer de telles actions. Vous pouvez donc utiliser les activités fournies par d'autres applications sur l'appareil, qui peuvent effectuer les actions pour vous.

C'est là que les intentions sont vraiment utiles. Vous pouvez créer un intent qui décrit une action que vous souhaitez effectuer, et le système lance l'activité appropriée à partir d'une autre application. Si plusieurs activités peuvent gérer l'intent, l'utilisateur peut sélectionner celle à utiliser. Par exemple, si vous souhaitez permettre à l'utilisateur d'envoyer un e-mail, vous pouvez créer l'intention suivante :

Kotlin

val intent = Intent(Intent.ACTION_SEND).apply {
    putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)

Java

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

L'extra EXTRA_EMAIL ajouté à l'intention est un tableau de chaînes d'adresses e-mail auxquelles l'e-mail doit être envoyé. Lorsqu'une application de messagerie répond à cette intention, elle lit le tableau de chaînes fourni dans l'extra et place les adresses dans le champ "À" du formulaire de composition d'e-mail. Dans ce cas, l'activité de l'application de messagerie démarre, puis reprend lorsque l'utilisateur a terminé.

startActivityForResult

Il arrive parfois que vous souhaitiez obtenir un résultat d'une activité à la fin de celle-ci. Par exemple, vous pouvez démarrer une activité qui permet à l'utilisateur de choisir une personne dans une liste de contacts. Une fois l'opération terminée, elle renvoie la personne sélectionnée. Pour ce faire, vous appelez la méthode startActivityForResult(Intent, int), où le paramètre entier identifie l'appel.

Cet identifiant est destiné à faire la distinction entre plusieurs appels à startActivityForResult(Intent, int) à partir de la même activité. Il ne s'agit pas d'un identifiant global et il ne risque pas d'entrer en conflit avec d'autres applications ou activités. Le résultat est renvoyé par votre méthode onActivityResult(int, int, Intent).

Lorsqu'une activité enfant se termine, elle peut appeler setResult(int) pour renvoyer des données à son parent. L'activité enfant doit fournir un code de résultat, qui peut être les résultats standards RESULT_CANCELED, RESULT_OK ou toute valeur personnalisée commençant par RESULT_FIRST_USER.

De plus, l'activité de l'enfant peut éventuellement renvoyer un objet Intent contenant toutes les données supplémentaires souhaitées. L'activité parent utilise la méthode onActivityResult(int, int, Intent), ainsi que l'identifiant entier fourni à l'origine par l'activité parent, pour recevoir les informations.

Si une activité enfant échoue pour une raison quelconque, par exemple en cas de plantage, l'activité parente reçoit un résultat avec le code RESULT_CANCELED.

Kotlin

class MyActivity : Activity() {
    // ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            // When the user center presses, let them pick a contact.
            startActivityForResult(
                    Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
                    PICK_CONTACT_REQUEST)
            return true
        }
        return false
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        when (requestCode) {
            PICK_CONTACT_REQUEST ->
                if (resultCode == RESULT_OK) {
                    // A contact was picked. Display it to the user.
                    startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
                }
        }
    }

    companion object {
        internal val PICK_CONTACT_REQUEST = 0
    }
}

Java

public class MyActivity extends Activity {
    // ...

    static final int PICK_CONTACT_REQUEST = 0;

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            // When the user center presses, let them pick a contact.
            startActivityForResult(
                new Intent(Intent.ACTION_PICK,
                new Uri("content://contacts")),
                PICK_CONTACT_REQUEST);
            return true;
        }
        return false;
    }

    protected void onActivityResult(int requestCode, int resultCode,
            Intent data) {
        if (requestCode == PICK_CONTACT_REQUEST) {
            if (resultCode == RESULT_OK) {
                // A contact was picked. Display it to the user.
                startActivity(new Intent(Intent.ACTION_VIEW, data));
            }
        }
    }
}

Coordonner les activités

Lorsqu'une activité en démarre une autre, elles subissent toutes les deux des transitions de cycle de vie. La première activité cesse de fonctionner et passe à l'état "En pause" ou "Arrêtée", tandis que l'autre activité est créée. Si ces activités partagent des données enregistrées sur le disque ou ailleurs, il est important de comprendre que la première activité n'est pas complètement arrêtée avant la création de la seconde. En effet, le processus de démarrage du deuxième chevauche celui d'arrêt du premier.

L'ordre des rappels de cycle de vie est bien défini, en particulier lorsque les deux activités se trouvent dans le même processus (c'est-à-dire la même application) et que l'une démarre l'autre. Voici l'ordre des opérations qui se produisent lorsque l'activité A démarre l'activité B :

  1. La méthode onPause de l'activité A s'exécute.
  2. Les méthodes onCreate, onStart et onResume de l'activité B s'exécutent séquentiellement. L'activité B reçoit désormais le focus de l'utilisateur.
  3. Si l'activité A n'est plus visible à l'écran, sa méthode onStop s'exécute.

Cette séquence de rappels de cycle de vie vous permet de gérer la transition des informations d'une activité à une autre.