Tâches et pile "Retour"

Une tâche est un ensemble d'activités avec lesquelles les utilisateurs interagissent lorsqu'ils essaient d'effectuer une action dans votre application. Ces activités sont organisées dans une pile appelée pile "Retour", dans l'ordre dans lequel chaque activité est ouverte.

Par exemple, une application de messagerie peut disposer d'une activité pour afficher une liste de nouveaux messages. Lorsque l'utilisateur sélectionne un message, une nouvelle activité s'ouvre pour afficher ce message. Cette nouvelle activité est ajoutée à la pile "Retour". Ensuite, lorsque l'utilisateur appuie ou renvoie vers le geste Retour, cette nouvelle activité se termine et est retirée de la pile.

Cycle de vie d'une tâche et de sa pile "Retour"

L'écran d'accueil de l'appareil est le point de départ de la plupart des tâches. Lorsqu'un utilisateur appuie sur l'icône d'une application ou d'un raccourci dans le lanceur d'applications ou sur l'écran d'accueil, la tâche de cette application s'affiche au premier plan. Si aucune tâche n'existe pour l'application, une tâche est créée et l'activité principale de cette application s'ouvre en tant qu'activité racine dans la pile.

Lorsque l'activité en cours en commence une autre, la nouvelle activité est placée en haut de la pile et devient active. L'activité précédente reste dans la pile, mais est arrêtée. Lorsqu'une activité est arrêtée, le système conserve l'état actuel de son interface utilisateur. Lorsque l'utilisateur effectue l'action "Retour", l'activité en cours est supprimée du haut de la pile et détruite. L'activité précédente reprend et l'état précédent de son UI est restauré.

Les activités de la pile ne sont jamais réorganisées. Elles ne sont transférées et retirées de la pile lorsqu'elles sont démarrées par l'activité en cours, puis ignorées par l'utilisateur via le bouton "Retour" ou le geste. Par conséquent, la pile "Retour" fonctionne comme une structure d'objet de type dernier arrivé, premier sorti. La figure 1 montre une chronologie avec des activités transférées vers une pile "Retour" et qui en sortent.

Figure 1. Représentation de la manière dont chaque nouvelle activité d'une tâche ajoute un élément à la pile "Retour". Lorsque l'utilisateur appuie ou revient en arrière, l'activité en cours est détruite et l'activité précédente reprend.

Au fur et à mesure que l'utilisateur continue d'appuyer sur l'écran ou de faire un geste pour revenir en arrière, chaque activité de la pile s'affiche pour révéler la précédente, jusqu'à ce que l'utilisateur retourne à l'écran d'accueil ou à l'activité en cours d'exécution lorsque la tâche a commencé. Lorsque toutes les activités sont supprimées de la pile, la tâche n'existe plus.

Comportement du geste Retour pour les activités du lanceur d'applications racine

Les activités du lanceur d'applications racine sont des activités qui déclarent un filtre d'intent avec ACTION_MAIN et CATEGORY_LAUNCHER. Ces activités sont uniques, car elles servent de points d'entrée à votre application à partir du lanceur d'applications et permettent de démarrer une tâche.

Lorsqu'un utilisateur appuie sur l'icône Retour depuis une activité du lanceur d'applications racine, le système gère l'événement différemment en fonction de la version d'Android exécutée par l'appareil.

Comportement du système sous Android 11 ou version antérieure
Le système termine l'activité.
Comportement du système sous Android 12 ou version ultérieure

Le système déplace l'activité et sa tâche en arrière-plan au lieu de les terminer. Ce comportement correspond au comportement par défaut du système lorsque vous quittez une application à l'aide du bouton d'accueil ou d'un geste.

Dans la plupart des cas, ce comportement signifie que les utilisateurs peuvent réactiver plus rapidement votre application à partir d'un état "tiède", au lieu d'avoir à la redémarrer complètement à froid.

Si vous devez fournir un retour arrière personnalisé, nous vous recommandons d'utiliser les API AndroidX Activity plutôt que de remplacer onBackPressed(). Les API AndroidX Activity s'appliquent automatiquement au comportement approprié du système si aucun composant n'intercepte le retour arrière du système.

Toutefois, si votre application ignore onBackPressed() pour gérer la navigation vers l'arrière et terminer l'activité, mettez à jour votre implémentation pour appeler super.onBackPressed() au lieu de terminer l'activité. Appeler super.onBackPressed() déplace l'activité et sa tâche en arrière-plan le cas échéant, et offre une expérience de navigation plus cohérente entre les applications.

Tâches en arrière-plan et au premier plan

Figure 2 : Deux tâches: la tâche B reçoit une interaction de l'utilisateur au premier plan, tandis que la tâche A est exécutée en arrière-plan, en attendant de reprendre.

Une tâche est une unité cohérente qui peut être déplacée en arrière-plan lorsqu'un utilisateur commence une nouvelle tâche ou accède à l'écran d'accueil. En arrière-plan, toutes les activités de la tâche sont arrêtées, mais sa pile "Retour" reste intacte. La tâche perd son focus tandis qu'une autre est en cours, comme illustré dans la figure 2. Une tâche peut ensuite revenir au premier plan pour que les utilisateurs puissent reprendre là où ils s'étaient arrêtés.

Prenons le flux de tâches suivant pour la tâche A actuelle, dont la pile comporte trois activités, dont deux sous l'activité en cours:

  1. L'utilisateur utilise le bouton ou le geste d'accueil, puis lance une nouvelle application à partir du lanceur d'applications.

    Lorsque l'écran d'accueil apparaît, la tâche A passe en arrière-plan. Lorsque la nouvelle application démarre, le système lance une tâche pour cette application (Tâche B) avec sa propre pile d'activités.

  2. Après avoir interagi avec cette application, l'utilisateur revient à l'écran d'accueil et sélectionne l'application qui a lancé la tâche A.

    La tâche A apparaît maintenant au premier plan : les trois activités de la pile sont intactes, et l'activité située en haut de la pile reprend. À ce stade, l'utilisateur peut également revenir à la tâche B en accédant à la page d'accueil et en sélectionnant l'icône de l'application qui a démarré cette tâche, ou en sélectionnant la tâche de l'application à partir de l'écran "Recents" (Éléments récents).

Plusieurs instances d'activité

Figure 3. Une même activité peut être instanciée plusieurs fois.

Étant donné que les activités de la pile "Retour" ne sont jamais réorganisées, si votre application permet aux utilisateurs de démarrer une activité particulière à partir de plusieurs activités, une instance de cette activité est créée et placée dans la pile, au lieu de placer toute instance précédente de l'activité en haut. Ainsi, une activité de votre application peut être instanciée plusieurs fois, même à partir de différentes tâches, comme illustré dans la figure 3.

Si l'utilisateur revient en arrière à l'aide du bouton ou du geste Retour, les instances de l'activité sont révélées dans l'ordre dans lequel elles ont été ouvertes, chacune ayant son propre état d'interface utilisateur. Toutefois, vous pouvez modifier ce comportement si vous ne souhaitez pas qu'une activité soit instanciée plusieurs fois. Pour en savoir plus, consultez la section sur la gestion des tâches.

Environnements multifenêtres

Lorsque des applications s'exécutent simultanément dans un environnement multifenêtre, compatible avec Android 7.0 (niveau d'API 24) ou version ultérieure, le système gère les tâches séparément pour chaque fenêtre. Chaque fenêtre peut comporter plusieurs tâches. Il en va de même pour les applications Android exécutées sur des Chromebooks: le système gère les tâches, ou groupes de tâches, par fenêtre.

Résumé du cycle de vie

Voici un résumé du comportement par défaut des activités et des tâches:

  • Lorsque l'activité A lance l'activité B, elle est arrêtée, mais le système conserve son état, tel que sa position de défilement et tout texte saisi dans des formulaires. Si l'utilisateur appuie ou utilise le geste Retour pendant l'activité B, l'activité A reprend et son état est restauré.

  • Lorsque l'utilisateur quitte une tâche à l'aide du bouton d'accueil ou du geste, l'activité en cours est arrêtée et sa tâche passe en arrière-plan. Le système conserve l'état de chaque activité de la tâche. Si l'utilisateur reprend ultérieurement la tâche en sélectionnant l'icône de lanceur qui l'a lancée, celle-ci passe au premier plan et reprend l'activité en haut de la pile.

  • Si l'utilisateur appuie ou revient en arrière, l'activité en cours est retirée de la pile et détruite. L'activité précédente de la pile reprend. Lorsqu'une activité est détruite, le système ne conserve pas l'état de l'activité.

    Ce comportement est différent pour les activités du lanceur d'applications racine lorsque votre application s'exécute sur un appareil équipé d'Android 12 ou version ultérieure.

  • Les activités peuvent être instanciées plusieurs fois, même à partir d'autres tâches.

Gérez des tâches

Android gère les tâches et la pile "Retour" en plaçant toutes les activités démarrées les unes après les autres dans la même tâche, dans une pile "dernier arrivé, premier sorti". Cela fonctionne parfaitement pour la plupart des applications, et vous n'avez généralement pas à vous soucier de la manière dont vos activités sont associées aux tâches ni de la façon dont elles existent dans la pile "Retour".

Cependant, vous pouvez décider d'interrompre le fonctionnement normal. Par exemple, vous pouvez souhaiter qu'une activité de votre application commence une nouvelle tâche au démarrage, au lieu d'être placée dans la tâche actuelle. Lorsque vous démarrez une activité, vous pouvez également transférer une instance existante de celle-ci au lieu de créer une instance au-dessus de la pile "Retour". Vous pouvez également souhaiter que votre pile "Retour" soit effacée de toutes les activités, sauf l'activité racine lorsque l'utilisateur quitte la tâche.

Vous pouvez effectuer ces opérations et bien d'autres en utilisant des attributs dans l'élément du fichier manifeste <activity> et des indicateurs dans l'intent que vous transmettez à startActivity().

Voici les principaux attributs <activity> que vous pouvez utiliser pour gérer les tâches:

Voici les indicateurs d'intent principal que vous pouvez utiliser:

Les sections suivantes expliquent comment utiliser ces attributs de fichier manifeste et ces indicateurs d'intent pour définir l'association des activités aux tâches et leur comportement dans la pile "Retour".

Les considérations relatives à la manière dont les tâches et les activités sont représentées et gérées dans l'écran "Recents" (Éléments récents) sont également abordés. Normalement, vous laissez le système définir la manière dont votre tâche et vos activités sont représentées dans l'écran "Recents" (Éléments récents), et vous n'avez pas besoin de modifier ce comportement. Pour en savoir plus, consultez la section Écran "Recents" (Éléments récents).

Définir les modes de lancement

Les modes de lancement vous permettent de définir l'association de la nouvelle instance d'une activité à la tâche en cours. Vous pouvez définir les modes de lancement de deux manières, décrites dans les sections suivantes:

  • Utiliser le fichier manifeste

    Lorsque vous déclarez une activité dans le fichier manifeste, vous pouvez spécifier la manière dont elle est associée aux tâches au moment où elle démarre.

  • Utiliser des options d'intent

    Lorsque vous appelez startActivity(), vous pouvez inclure un indicateur dans le Intent qui déclare comment (ou si) la nouvelle activité est associée à la tâche actuelle.

Ainsi, si l'activité A lance l'activité B, l'activité B peut définir dans son fichier manifeste la manière dont elle s'associe à la tâche en cours, et l'activité A peut utiliser un indicateur d'intent pour demander comment l'activité B peut s'associer à la tâche en cours.

Si les deux activités définissent la manière dont l'activité B s'associe à une tâche, la requête de l'activité A, telle que définie dans l'intent, est satisfaite sur la requête de l'activité B, telle que définie dans son fichier manifeste.

Définir les modes de lancement à l'aide du fichier manifeste

Lorsque vous déclarez une activité dans votre fichier manifeste, vous pouvez spécifier la manière dont elle est associée à une tâche à l'aide de l'attribut launchMode de l'élément <activity>.

Vous pouvez attribuer cinq modes de lancement à l'attribut launchMode:

  1. "standard"
    Mode par défaut. Le système crée une instance de l'activité dans la tâche à partir de laquelle elle a été démarrée et y achemine l'intent. L'activité peut être instanciée plusieurs fois, chaque instance peut appartenir à différentes tâches et une tâche peut avoir plusieurs instances.
  2. "singleTop"
    Si une instance de l'activité existe déjà en haut de la tâche en cours, le système achemine l'intent vers cette instance via un appel à sa méthode onNewIntent(), au lieu de créer une instance de l'activité. L'activité est instanciée plusieurs fois, chaque instance peut appartenir à différentes tâches et une tâche peut avoir plusieurs instances (mais uniquement si l'activité en haut de la pile "Retour" n'est pas une instance existante de l'activité).

    Par exemple, supposons que la pile "Retour" d'une tâche soit composée de l'activité racine A, avec les activités B, C et D en haut (la pile est donc A-B-C-D, avec D en haut). Un intent arrive pour une activité de type D. Si D dispose du mode de lancement "standard" par défaut, une nouvelle instance de la classe est lancée et la pile devient A-B-C-D-D. Toutefois, si le mode de lancement de D est "singleTop", l'instance existante de D reçoit l'intent via onNewIntent(), car elle se trouve en haut de la pile et reste A-B-C-D. Si, en revanche, un intent arrive pour une activité de type B, une nouvelle instance de B est ajoutée à la pile, même si son mode de lancement est "singleTop".

  3. "singleTask"
    Le système crée l'activité à la racine d'une nouvelle tâche ou la localise sur une tâche existante avec la même affinité. Si une instance de l'activité existe déjà, le système achemine l'intent vers l'instance existante via un appel à sa méthode onNewIntent() au lieu de créer une instance. Pendant ce temps, toutes les autres activités qui s'y trouvent sont détruites.
  4. "singleInstance".
    Le comportement est le même que pour "singleTask", sauf que le système ne lance aucune autre activité dans la tâche contenant l'instance. L'activité est toujours le seul et unique membre de sa tâche. Toutes les activités démarrées par celle-ci s'ouvrent dans une tâche distincte.
  5. "singleInstancePerTask".
    L'activité ne peut s'exécuter qu'en tant qu'activité racine de la tâche, c'est-à-dire la première activité qui l'a créée. Il ne peut donc y avoir qu'une seule instance de cette activité dans une tâche. Contrairement au mode de lancement singleTask, cette activité peut être démarrée dans plusieurs instances dans des tâches différentes si l'option FLAG_ACTIVITY_MULTIPLE_TASK ou FLAG_ACTIVITY_NEW_DOCUMENT est définie.

Autre exemple, l'application du navigateur Android déclare que l'activité du navigateur Web s'ouvre toujours dans sa propre tâche. Pour ce faire, spécifiez le mode de lancement singleTask dans l'élément <activity>. Cela signifie que si votre application émet un intent pour ouvrir le navigateur Android, son activité n'est pas placée dans la même tâche que votre application. Au lieu de cela, une nouvelle tâche démarre pour le navigateur ou, si le navigateur a déjà une tâche en cours d'exécution en arrière-plan, cette tâche est transférée pour gérer le nouvel intent.

Qu'une activité commence dans une nouvelle tâche ou dans la même tâche que l'activité qui l'a lancée, le bouton et le geste Retour redirigent toujours l'utilisateur vers l'activité précédente. Toutefois, si vous démarrez une activité qui spécifie le mode de lancement singleTask et qu'une instance de cette activité existe dans une tâche en arrière-plan, cette tâche entière est mise au premier plan. À ce stade, la pile "Retour" inclut toutes les activités de la tâche déplacée en haut de la pile. La figure 4 illustre ce type de scénario.

Figure 4. Représentation de la manière dont une activité avec le mode de lancement "singleTask" est ajoutée à la pile "Retour". Si l'activité fait déjà partie d'une tâche en arrière-plan avec sa propre pile "Retour", cette pile "Retour" entière s'affiche également au-dessus de la tâche en cours.

Pour en savoir plus sur l'utilisation des modes de lancement dans le fichier manifeste, consultez la documentation sur l'élément <activity>.

Définir des modes de lancement à l'aide d'indicateurs d'intent

Lorsque vous démarrez une activité, vous pouvez modifier l'association par défaut d'une activité à sa tâche en incluant des indicateurs dans l'intent que vous envoyez à startActivity(). Les options que vous pouvez utiliser pour modifier le comportement par défaut sont les suivantes:

FLAG_ACTIVITY_NEW_TASK

Le système lance l'activité dans une nouvelle tâche. Si une tâche est déjà en cours d'exécution pour l'activité en cours de démarrage, elle est mise au premier plan avec son dernier état restauré, et l'activité reçoit le nouvel intent dans onNewIntent().

Cela produit le même comportement que la valeur "singleTask" launchMode décrite dans la section précédente.

FLAG_ACTIVITY_SINGLE_TOP

Si l'activité en cours de démarrage est en haut de la pile "Retour", l'instance existante reçoit un appel à onNewIntent() au lieu de créer une instance de l'activité.

Cela produit le même comportement que la valeur "singleTop" launchMode abordée dans la section précédente.

FLAG_ACTIVITY_CLEAR_TOP

Si l'activité en cours d'exécution s'exécute déjà dans la tâche en cours, au lieu de lancer une nouvelle instance de cette activité, le système détruit toutes les autres activités qui s'y rapportent. L'intent est transmis à l'instance réactivée de l'activité, désormais en haut, via onNewIntent().

Aucune valeur n'est associée à l'attribut launchMode pour générer ce comportement.

FLAG_ACTIVITY_CLEAR_TOP est généralement utilisé conjointement avec FLAG_ACTIVITY_NEW_TASK. Lorsqu'ils sont utilisés ensemble, ces indicateurs permettent de localiser une activité existante dans une autre tâche et de la placer dans une position permettant de répondre à l'intent.

Gérer les affinités

Une affinité indique la tâche à laquelle une activité "préfère" appartenir. Par défaut, toutes les activités d'une même application ont une affinité les unes avec les autres : elles "préfèrent" se trouver dans la même tâche.

Toutefois, vous pouvez modifier l'affinité par défaut d'une activité. Les activités définies dans différentes applications peuvent partager une affinité, et différentes affinités de tâches peuvent être attribuées aux activités définies dans la même application.

Vous pouvez modifier l'affinité d'une activité à l'aide de l'attribut taskAffinity de l'élément <activity>.

L'attribut taskAffinity utilise une valeur de chaîne qui doit être différente du nom de package par défaut déclaré dans l'élément <manifest>, car le système utilise ce nom pour identifier l'affinité de tâche par défaut pour l'application.

L'affinité intervient dans deux cas:

  1. Lorsque l'intent qui lance une activité contient l'indicateur FLAG_ACTIVITY_NEW_TASK.

    Par défaut, une nouvelle activité est lancée dans la tâche de l'activité appelée startActivity(). Il est placé sur la même pile "Retour" que l'appelant.

    Toutefois, si l'intent transmis à startActivity() contient l'indicateur FLAG_ACTIVITY_NEW_TASK, le système recherche une autre tâche pour héberger la nouvelle activité. Souvent, il s’agit d’une nouvelle tâche. Toutefois, ce n'est pas une obligation. S'il existe une tâche avec la même affinité que la nouvelle activité, celle-ci est lancée dans cette tâche. Sinon, il commence une nouvelle tâche.

    Si cet indicateur entraîne le début d'une nouvelle tâche et que l'utilisateur utilise le bouton d'accueil ou le geste pour la quitter, il doit pouvoir revenir à la tâche. Certaines entités, telles que le gestionnaire de notifications, démarrent toujours des activités dans une tâche externe, jamais en tant que leur propre. Par conséquent, elles placent toujours FLAG_ACTIVITY_NEW_TASK dans les intents qu'elles transmettent à startActivity().

    Si une entité externe susceptible d'utiliser cet indicateur peut appeler votre activité, assurez-vous que l'utilisateur dispose d'un moyen indépendant pour revenir à la tâche démarrée, par exemple avec une icône de lanceur, où l'activité racine de la tâche possède un filtre d'intent CATEGORY_LAUNCHER. Pour en savoir plus, consultez la section sur le démarrage des tâches.

  2. Lorsque l'attribut allowTaskReparenting d'une activité est défini sur "true".

    Dans ce cas, l'activité peut passer de la tâche qu'elle démarre à la tâche avec laquelle elle a une affinité lorsqu'elle passe au premier plan.

    Par exemple, supposons qu'une activité qui signale les conditions météorologiques dans des villes sélectionnées soit définie dans le cadre d'une application de voyage. Elle présente la même affinité que les autres activités de la même application (l'affinité d'application par défaut), et peut changer de parent avec cet attribut.

    Lorsque l'une de vos activités démarre l'activité de générateur météorologique, elle appartient initialement à la même tâche que votre activité. Toutefois, lorsque la tâche de l'application de voyage passe au premier plan, l'activité du rapporteur météo est réaffectée à cette tâche et s'y affiche.

Effacer la pile "Retour"

Si l'utilisateur quitte une tâche pendant longtemps, le système la supprime de toutes les activités, à l'exception de l'activité racine. Lorsque l'utilisateur revient à la tâche, seule l'activité racine est restaurée. Le système se comporte de cette manière par rapport à l'hypothèse qu'après un certain temps, les utilisateurs ont abandonné ce qu'ils faisaient auparavant et reviennent à la tâche pour commencer quelque chose de nouveau.

Vous pouvez utiliser certains attributs d'activité pour modifier ce comportement:

alwaysRetainTaskState
Lorsque cet attribut est défini sur "true" dans l'activité racine d'une tâche, le comportement par défaut que nous venons de décrire ne se produit pas. La tâche conserve toutes les activités dans sa pile, même après une longue période.
clearTaskOnLaunch

Lorsque cet attribut est défini sur "true" dans l'activité racine d'une tâche, la tâche est effacée jusqu'à l'activité racine chaque fois que l'utilisateur la quitte et y retourne. En d'autres termes, c'est l'opposé de alwaysRetainTaskState. L'utilisateur revient toujours à la tâche dans son état initial, même après l'avoir quittée pendant un moment seulement.

finishOnTaskLaunch

Cet attribut est semblable à clearTaskOnLaunch, mais il fonctionne sur une seule activité et non sur une tâche entière. Cela peut également entraîner l'arrêt de toute activité, à l'exception de l'activité racine. Lorsqu'elle est définie sur "true", l'activité reste intégrée à la tâche uniquement pour la session en cours. Si l'utilisateur quitte la tâche, puis y retourne, elle n'est plus présente.

Démarrer une tâche

Vous pouvez configurer une activité comme point d'entrée d'une tâche en lui attribuant un filtre d'intent avec "android.intent.action.MAIN" comme action spécifiée et "android.intent.category.LAUNCHER" comme catégorie spécifiée:

<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>

Un filtre d'intent de ce type entraîne l'affichage d'une icône et d'un libellé pour l'activité dans le lanceur d'applications. Les utilisateurs peuvent ainsi lancer l'activité et revenir à la tâche créée à tout moment après son lancement.

Cette deuxième capacité est importante. Les utilisateurs doivent pouvoir quitter une tâche, puis y revenir plus tard à l'aide de ce lanceur d'activités. Pour cette raison, n'utilisez que les deux modes de lancement qui marquent les activités comme lançant toujours une tâche, "singleTask" et "singleInstance", lorsque l'activité est associée aux filtres ACTION_MAIN et CATEGORY_LAUNCHER.

Imaginez, par exemple, ce qui peut se produire en l'absence de filtre: un intent lance une activité "singleTask", lance une nouvelle tâche et l'utilisateur passe un certain temps à travailler dessus. L'utilisateur utilise ensuite le bouton d'accueil ou le geste. La tâche est maintenant envoyée en arrière-plan et n'est pas visible. L'utilisateur n'a désormais aucun moyen de revenir à la tâche, car elle n'est pas représentée dans le lanceur d'applications.

Si vous ne souhaitez pas que l'utilisateur puisse revenir à une activité, définissez finishOnTaskLaunch de l'élément <activity> sur "true". Pour en savoir plus, consultez la section Effacer la pile "Retour".

D'autres informations sur la représentation et la gestion des tâches et des activités dans l'écran "Recents" (Éléments récents) sont disponibles sur l'écran "Recents" (Éléments récents).

Autres ressources