Cycle de vie d'une activité

Lorsqu'un utilisateur parcourt votre application, la quitte et y retourne, 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 lorsqu'un état change, ou que le système est en train de créer, d'arrêter ou de reprendre une activité, ou de détruire le processus dans lequel se trouve l'activité.

Dans les méthodes de rappel de cycle de vie, vous pouvez déclarer le comportement de votre activité lorsque l'utilisateur la quitte et la retourne. Par exemple, si vous créez un lecteur vidéo en streaming, vous pouvez mettre la vidéo en pause et interrompre 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 lecture de la vidéo au même endroit.

Chaque rappel vous permet d'effectuer un travail spécifique adapté à un changement d'état donné. Faire le bon travail au bon moment et gérer correctement les transitions rend votre application plus robuste et plus 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 l'utilisation de votre application.
  • Consommer de précieuses ressources système lorsque l'utilisateur ne l'utilise pas activement.
  • La progression de l'utilisateur est perdue s'il quitte votre application pour y revenir plus tard.
  • Plantage ou perte de la progression de l'utilisateur lorsque l'écran passe du mode paysage au mode portrait, et vice versa.

Ce document explique en détail le cycle de vie d'une activité. Ce document commence par décrire le paradigme du cycle de vie. Ensuite, il explique chacun des rappels : ce qui se passe en interne pendant 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'activité et la vulnérabilité d'un processus à la suppression du 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 des conseils sur les bonnes pratiques, consultez Gérer les cycles de vie à l'aide de composants tenant compte des cycles de vie et Enregistrer les états de l'interface utilisateur. Pour découvrir comment concevoir une application robuste et de qualité professionnelle en combinant des activités avec des composants d'architecture, consultez le guide de l'architecture des applications.

Concepts du cycle de vie d'une activité

Pour gérer les transitions entre les étapes du cycle de vie de l'activité, la classe Activity fournit un ensemble principal 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 est 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émonter. Dans certains cas, l'activité n'est que partiellement démontée et reste 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 s'était arrêté. À quelques exceptions près, les applications ne peuvent pas démarrer d'activités lorsqu'elles s'exécutent en arrière-plan.

La probabilité qu'un système arrête un processus donné, ainsi que les activités qu'il contient, dépendent de l'état de l'activité à un moment donné. Pour en savoir plus sur la relation entre l'état et la vulnérabilité à l'éjection, consultez la section État d'activité et é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. Cependant, il est important que vous les compreniez et que vous implémentez celles qui font que votre application se comporte comme prévu.

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 d'une activité. Toutefois, placez le code qui implémente les actions d'un composant dépendant dans le composant, plutôt que la méthode du cycle de vie de l'activité. Pour ce faire, vous devez rendre le composant dépendant sensible au cycle de vie. Pour savoir comment rendre vos composants dépendants sensibles au cycle de vie, consultez Gérer les cycles de vie à l'aide de composants tenant compte des cycles 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éée. Dans la méthode onCreate(), effectuez une logique de démarrage d'application de base qui ne se produit qu'une seule fois pendant 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 niveau d'accès aux classes. Cette méthode reçoit le paramètre savedInstanceState, qui est un objet Bundle contenant l'état précédemment enregistré de l'activité. Si l'activité n'a jamais existé auparavant, la valeur de l'objet Bundle est nulle.

Si un composant qui tient compte des cycles de vie est connecté 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 afin que votre composant tenant compte du cycle de vie puisse effectuer tout 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 de membre et la configuration d'une partie de l'interface utilisateur. Dans cet exemple, le fichier de mise en page XML transmet l'ID de ressource R.layout.main_activity du fichier à 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 votre code d'activité et créer une hiérarchie des vues en insérant de nouveaux objets View dans un ViewGroup. Vous pouvez ensuite utiliser cette mise en page en transmettant la racine ViewGroup à setContentView(). Pour en savoir plus sur la création d'une interface utilisateur, consultez la documentation concernant l'interface utilisateur.

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

onStart()

Lorsque l'activité passe à l'état "Démarrée", le système appelle onStart(). Cet appel rend l'activité visible par l'utilisateur lorsque l'application se prépare pour qu'elle passe au premier plan et devienne interactive. Par exemple, cette méthode permet d'initialiser le code qui gère l'UI.

Lorsque l'activité passe à l'état "Démarrée", tout composant sensible au cycle de vie lié au cycle de vie de l'activité reçoit l'événement ON_START.

La méthode onStart() s'exécute rapidement et, comme avec l'état "Created", l'activité ne reste pas à l'état "Started" (Démarrée). 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 s'affiche 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 que quelque chose la éloigne de l'application, comme l'appareil qui reçoit un appel téléphonique, l'utilisateur qui accède à une autre activité ou l'écran de l'appareil qui s'éteint.

Lorsque l'activité passe à l'état "Reprise", tout composant sensible au cycle de vie lié 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 toutes les fonctionnalités qui doivent s'exécuter lorsque le composant est visible et au premier plan, comme le démarrage d'un aperçu de l'appareil photo.

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

Si l'activité revient à l'état "Reprise" à partir de l'état "Mis en 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 de reprise.

Voici un exemple de composant sensible au cycle de vie qui accède à la caméra lorsqu'il 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. Toutefois, 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 réactivée (visible et active au premier plan), initialisez-la après l'événement ON_RESUME montré précédemment. Si vous souhaitez que la caméra reste active pendant que l'activité est en pause, mais visible, par exemple en mode multifenêtre, initialisez-la après l'événement ON_START.

Toutefois, le fait d'avoir la caméra active pendant que votre activité est suspendue peut empêcher l'accès à la caméra à une autre application réactivée en mode multifenêtre. Parfois, il est nécessaire de maintenir la caméra active pendant que votre activité est 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 le plus approprié pour prendre le contrôle des ressources système partagées dans le contexte du mode multifenêtre. Pour en savoir plus sur la compatibilité du mode multifenêtre, consultez la page Compatibilité avec le mode multifenêtre.

Quel que soit l'événement de compilation dans lequel vous choisissez d'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 arrêtez-le après l'événement ON_STOP. Si vous l'initialisez après l'événement ON_RESUME, relâchez-le après l'événement ON_PAUSE.

L'extrait de code précédent place le code d'initialisation de l'appareil photo dans un composant tenant compte des cycles de vie. Vous pouvez placer ce code directement dans les rappels de cycle de vie d'une activité, tels que onStart() et onStop(), mais nous vous le déconseillons. L'ajout de cette logique à un composant indépendant qui tient compte du cycle de vie vous permet de le réutiliser dans plusieurs activités sans avoir à dupliquer le code. Pour savoir comment créer un composant qui tient compte des cycles de vie, consultez la section Gérer les cycles de vie à l'aide de composants tenant compte des cycles de vie.

onPause()

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

  • Un événement qui interrompt l'exécution de l'application, comme décrit dans la section sur le rappel onResume(), suspend l'activité en cours. C'est le 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é couverte. Tant que l'activité est partiellement visible, mais pas au premier plan, elle reste en veille.

Lorsqu'une activité passe à l'état "Mise en pause", tout composant sensible au 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 tant que le composant n'est pas au premier plan, comme l'arrêt d'un aperçu de la caméra.

Utilisez la méthode onPause() pour suspendre ou ajuster les opérations qui ne peuvent pas se poursuivre ou qui peuvent se poursuivre avec la modération lorsque le Activity est en pause et que vous prévoyez de reprendre prochainement.

Vous pouvez également utiliser la méthode onPause() pour libérer des ressources système, des identifiants pour les capteurs (comme le GPS) ou toute ressource qui affecte l'autonomie de la batterie lorsque votre activité est 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 rester entièrement visible si l'application est en mode multifenêtre. Envisagez d'utiliser onStop() au lieu de onPause() pour libérer ou ajuster entièrement 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 l'équivalent de l'exemple d'événement ON_RESUME précédent, en libérant 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 libération de l'appareil photo après la réception de l'événement ON_PAUSE par LifecycleObserver.

L'exécution de onPause() est très brève et n'offre pas nécessairement assez de temps pour effectuer les opérations d'enregistrement. Pour cette raison, n'utilisez pas onPause() pour enregistrer des données d'application ou d'utilisateur, effectuer des appels réseau ou exécuter des transactions de base de données. Il est possible que ces tâches ne se terminent pas avant la fin de la méthode.

Effectuez plutôt des opérations d'arrêt lourdes 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 Enregistrer et restaurer l'état.

L'achèvement 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 soit réactivée ou qu'elle devienne complètement invisible pour l'utilisateur. Si l'activité reprend, le système appelle à nouveau le rappel onResume().

Si l'activité repasse de l'état "Mis en pause" à l'état "Reprise", le système conserve l'instance Activity en mémoire et la rappelle lorsque le système appelle onResume(). Dans ce scénario, vous n'avez pas besoin de réinitialiser les composants créés lors de l'une des méthodes de rappel conduisant à l'état de 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 la totalité de l'écran. Le système appelle également onStop() lorsque l'exécution de l'activité est terminée et qu'elle est sur le point d'être arrêtée.

Lorsque l'activité passe à l'état "Arrêtée", tout composant sensible au 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 tant que 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 suspendre des animations ou passer d'une mise à jour de position précise à sommaire. Utiliser onStop() au lieu de onPause() signifie que le travail lié à l'interface utilisateur se poursuit, même lorsque l'utilisateur consulte votre activité en mode multifenêtre.

Utilisez également onStop() pour effectuer des opérations d'arrêt qui utilisent de manière relativement intensive le 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'un brouillon de note 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 Room et découvrir comment l'implémenter dans votre application, consultez le guide sur la bibliothèque Room Persistence.

Lorsque votre activité passe à l'état "Arrêtée", l'objet Activity reste résident en mémoire: il conserve toutes les informations sur l'état et les membres, 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 conduisant à l'état de reprise. Le système suit également l'état actuel de chaque objet View de la mise en page. Par conséquent, si l'utilisateur saisit du texte dans un widget EditText, ce contenu est conservé. Vous n'avez donc pas besoin de l'enregistrer ni de le restaurer.

Remarque : Une fois votre activité arrêtée, le système peut détruire le processus contenant l'activité si le système doit récupérer de la mémoire. Même si le système détruit le processus pendant que l'activité est arrêtée, il conserve l'état des objets View, tels que le texte d'un widget EditText, dans un Bundle (un blob de paires clé/valeur) et les restaure si l'utilisateur revient à l'activité. Pour en savoir plus sur la restauration d'une activité renvoyée par un utilisateur, consultez la section Enregistrer et restaurer l'état.

À partir de l'état "Arrêtée", soit l'activité réagit pour interagir avec l'utilisateur, soit l'activité est terminée et disparaît. Si l'activité est rétablie, 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é est en cours de finalisation, car l'utilisateur l'a complètement ignorée ou finish() est appelé au niveau de l'activité.
  2. Le système détruit temporairement l'activité en raison d'une modification de la configuration, telle qu'une rotation de l'appareil ou l'activation du mode multifenêtre.

Lorsque l'activité passe à l'état détruite, tout composant sensible au 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 la destruction de Activity.

Au lieu de mettre la logique dans votre Activity pour déterminer pourquoi il est détruit, utilisez un objet ViewModel pour contenir les données de vue pertinentes pour votre Activity. Si le 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() de ViewModel est appelée, ce qui permet de nettoyer toutes les données nécessaires avant la destruction. Vous pouvez distinguer ces deux scénarios avec la méthode isFinishing().

Si l'activité se termine, onDestroy() est le dernier rappel de cycle de vie reçu par l'activité. 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, telles que onStop().

État de l'activité et éjection de la mémoire

Le système arrête les processus lorsqu'il doit libérer de la RAM. La probabilité que le système arrête un processus donné dépend de l'état du processus à un moment donné. L'état du processus dépend à son tour de l'état de l'activité en cours d'exécution dans le processus. Le tableau 1 montre les corrélations entre l'état du processus, l'état d'activité et la probabilité que le système arrête le processus. Ce tableau ne s'applique que si un processus n'exécute pas d'autres types de composants d'application.

Risque de mort État du processus État de l'activité finale
Le plus faible Premier plan (acquis ou sur le point d'être sélectionné) A repris
Faible Visible (pas de sélection) Démarrée/En pause
Meilleure qualité Arrière-plan (invisible) Arrêté
La plus élevée Vide Détruit

Tableau 1. Relation entre le cycle de vie d'un processus et l'état d'activité

Le système ne tue jamais une activité directement pour libérer de la mémoire. Au lieu de cela, il tue le processus dans lequel l'activité s'exécute, en détruisant non seulement l'activité, mais également tout le reste en cours d'exécution dans le processus. Pour savoir comment conserver et restaurer l'état de l'interface utilisateur de votre activité en cas d'arrêt d'un processus déclenché par le système, consultez la section Enregistrer et restaurer l'état.

L'utilisateur peut également fermer un processus à l'aide du gestionnaire d'applications, sous "Paramètres", pour fermer l'application correspondante.

Pour en savoir plus sur les processus, consultez la page Présentation des processus et des threads.

Enregistrer et restaurer l'état temporaire de l'interface utilisateur

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

De même, un utilisateur s'attend à ce que l'état de l'interface utilisateur reste le même s'il passe temporairement de votre application à une autre, puis la revient plus tard. Toutefois, 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étruit l'activité, conservez l'état temporaire de l'interface utilisateur de l'utilisateur à l'aide d'une combinaison de ViewModel, onSaveInstanceState() et/ou de stockage local. Pour en savoir plus sur les attentes des utilisateurs par rapport au comportement du système et pour savoir comment préserver au mieux les données d'état complexes de l'interface utilisateur en cas d'activité déclenchée par le système et d'arrêt de processus, consultez Enregistrer les états de l'interface utilisateur.

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 uniquement onSaveInstance() pour conserver l'état de l'UI en cas de modifications de configuration ou d'arrêt de processus initié par le système. Toutefois, comme onSaveInstance() entraîne des coûts de sérialisation/désérialisation, vous utilisez dans la plupart des cas à la fois ViewModel et onSaveInstance(), comme indiqué dans la section Enregistrer les états de l'interface utilisateur.

Remarque : Pour en savoir plus sur les modifications de configuration, sur la manière de limiter la recréation d'activité si nécessaire et sur la manière de réagir à ces modifications de configuration à partir du système de vues et de Jetpack Compose, consultez la page Gérer les modifications de configuration.

É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, le concept du système et celui de l'utilisateur concernant cette instance Activity sont définitivement supprimés. Dans ces scénarios, les attentes de l'utilisateur correspondent 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 de la mémoire), bien que l'instance Activity réelle ait 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écrit 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 d'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 d'instance Bundle pour enregistrer des informations sur chaque objet View de la mise en page de votre activité, telles que la valeur textuelle saisie 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 besoin de code. Toutefois, votre activité peut contenir d'autres informations d'état que vous souhaitez restaurer, telles que des variables de membre qui suivent la progression de l'utilisateur dans l'activité.

Remarque : Pour que le système Android restaure l'état des vues de votre activité, chaque vue doit avoir un ID unique, fourni par l'attribut android:id.

Un objet Bundle n'est pas adapté à la conservation d'une quantité insignifiante de données, car il nécessite une sérialisation sur le thread principal et consomme de la mémoire de processus système. Si vous souhaitez conserver au-delà d'une très petite quantité de données, adoptez une approche combinée à l'aide du stockage local persistant, de la méthode onSaveInstanceState() et de la classe ViewModel, comme indiqué dans la section Enregistrer les états de l'interface utilisateur.

Enregistrer l'état de l'interface utilisateur 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'états d'instances. L'implémentation par défaut de cette méthode enregistre des informations temporaires sur l'état de la hiérarchie des 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 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);
}

Remarque : onSaveInstanceState() n'est pas appelé lorsque l'utilisateur ferme explicitement l'activité ni, dans d'autres cas, lorsque finish() est appelé.

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

Restaurer l'état de l'interface utilisateur de l'activité à l'aide de l'état d'instance enregistré

Lorsque votre activité est recréée après sa destruction, vous pouvez récupérer l'état de l'instance enregistré à partir du Bundle que le système transmet à votre activité. Les méthodes de rappel onCreate() et onRestoreInstanceState() reçoivent le même Bundle qui contient les informations d'état de l'instance.

Étant donné que la méthode onCreate() est appelée si le système crée une instance de votre activité ou recrée une instance précédente, vous devez vérifier si l'état Bundle est nul avant d'essayer de le lire. Si la valeur 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 n'appelle onRestoreInstanceState() que s'il existe un état enregistré à restaurer. Vous n'avez donc pas besoin de vérifier si la valeur Bundle est nulle.

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);
}

Attention: Appelez toujours l'implémentation de super-classe de onRestoreInstanceState() afin que l'implémentation par défaut puisse restaurer l'état de la hiérarchie des vues.

Navigation entre les activités

Une application est susceptible d'entrer et de quitter une activité, peut-être plusieurs fois, pendant sa durée de vie, par exemple lorsque l'utilisateur appuie sur le bouton "Retour" de l'appareil ou que l'activité lance une autre activité.

Cette section aborde les sujets que vous devez connaître pour mettre en œuvre des transitions d'activité réussies. Ces sujets incluent le démarrage d'une activité à partir d'une autre, l'enregistrement de l'état et la restauration de l'état.

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

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

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 pouvez la lancer à l'aide de la méthode startActivity() ou startActivityForResult(). Dans les deux cas, vous transmettez un objet Intent.

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

startActivity()

Si l'activité nouvellement démarrée n'a pas besoin de renvoyer un 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 souhaiter effectuer certaines actions, comme envoyer un e-mail, un SMS 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 ces actions à votre place.

C'est là que les intents sont très utiles. Vous pouvez créer un intent qui décrit une action que vous souhaitez effectuer. Le système lancera ensuite l'activité appropriée à partir d'une autre application. Si plusieurs activités peuvent gérer l'intent, l'utilisateur peut sélectionner celle qu'il souhaite utiliser. Par exemple, si vous souhaitez autoriser l'utilisateur à envoyer un e-mail, vous pouvez créer l'intent suivant:

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'élément supplémentaire EXTRA_EMAIL ajouté à l'intent est un tableau de chaînes d'adresses e-mail auxquelles l'e-mail doit être envoyé. Lorsqu'une application de messagerie répond à cet intent, elle lit le tableau de chaînes fourni dans l'extra et place les adresses dans le champ "À" du formulaire de composition de l'e-mail. Dans ce cas, l'activité de l'application de messagerie commence et, lorsque l'utilisateur a terminé, elle reprend.

startActivityForResult()

Il peut arriver que vous souhaitiez obtenir un résultat à partir d'une activité lorsqu'elle se termine. Par exemple, vous pouvez lancer une activité permettant à l'utilisateur de choisir une personne dans une liste de contacts. Lorsqu'il se termine, il renvoie la personne sélectionnée. Pour ce faire, appelez la méthode startActivityForResult(Intent, int), dans laquelle le paramètre entier identifie l'appel.

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

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

En outre, l'activité enfant peut éventuellement renvoyer un objet Intent contenant les données supplémentaires de son choix. Pour recevoir les informations, l'activité parente utilise la méthode onActivityResult(int, int, Intent), ainsi que l'identifiant entier fourni à l'origine par l'activité parente.

Si une activité enfant échoue pour une raison quelconque, telle qu'un 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));
             }
         }
     }
 }

Activités de coordination

Lorsqu'une activité en commence une autre, elle subit toutes les deux des transitions de cycle de vie. La première activité cesse de fonctionner et passe à l'état "Mise en pause" ou "Arrêtée", tandis que l'autre activité est créée. Dans le cas où ces activités partagent des données enregistrées sur un 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. À la place, le processus de démarrage de la deuxième icône fait chevaucher le processus d'arrêt de la première.

L'ordre des rappels de cycle de vie est bien défini, en particulier lorsque les deux activités sont dans le même processus (en d'autres termes, 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 dans l'ordre. L'activité B est désormais axée sur 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.