222 votes

L'application redémarre plutôt que de reprendre.

Hopefully someone can help me figure out, if not a solution, at least an explanation for a behaviour.

Le Problème :

Sur certains appareils, en appuyant sur l'icône du lanceur, la tâche en cours est reprise, sur d'autres, cela entraîne le lancement de l'intention de lancement initiale (redémarrage de l'application). Pourquoi cela se produit-il ?

Le Détail :

Lorsque vous appuyez sur l'icône du "Lanceur", l'application démarre normalement - c'est-à-dire, je suppose, qu'une intention est lancée avec le nom de votre première Activity avec l'action android.intent.action.MAIN et la catégorie android.intent.category.LAUNCHER. Cela ne peut cependant pas toujours être le cas :

Sur la majorité des appareils, si vous appuyez sur l'icône du lanceur après que l'application est déjà en cours d'exécution, l'activité en cours d'exécution dans ce processus est reprise (PAS l'activité initiale). Elle reprend de la même manière que si vous l'aviez sélectionnée dans les "Tâches récentes" dans le menu du système d'exploitation. C'est le comportement que je veux sur tous les appareils.

Cependant, sur certains autres appareils, un comportement différent se produit :

  • Sur le Motorola Xoom, lorsque vous appuyez sur l'icône du lanceur, l'application démarrera toujours l'activité de lancement initiale, peu importe ce qui est en cours d'exécution. Je suppose que les icônes de lancement démarrent toujours l'intention "LAUNCHER".

  • Sur le Samsung Tab 2, lorsque vous appuyez sur l'icône du lanceur, si vous venez d'installer l'application, elle lancera toujours l'activité initiale (identique au Xoom) - cependant, après avoir redémarré l'appareil après l'installation, l'icône du lanceur reprendra l'application. Je suppose que ces appareils ajoutent les "applications installées" dans une table de recherche au démarrage de l'appareil, ce qui permet aux icônes du lanceur de reprendre correctement les tâches en cours d'exécution ?

J'ai lu de nombreuses réponses qui semblent similaires à mon problème mais simplement ajouter android:alwaysRetainTaskState="true" ou utiliser launchMode="singleTop" pour l'Activity ne sont pas la réponse.

Éditer :

Après le dernier lancement de cette application, nous constatons que ce comportement a commencé à se produire sur tous les appareils après le premier redémarrage. Ce qui me semble fou mais en examinant le processus de redémarrage, je ne trouve pas ce qui ne va pas.

278voto

blacksh33p Points 941

Le comportement que vous rencontrez est causé par un problème qui existe dans certains lanceurs Android depuis l'API 1. Vous pouvez trouver des détails sur le bug ainsi que des solutions possibles ici : https://code.google.com/p/android/issues/detail?id=2373.

C'est un problème assez courant sur les appareils Samsung ainsi que sur d'autres fabricants qui utilisent un lanceur/personnalisation personnalisé(e). Je n'ai pas vu le problème se produire sur un lanceur Android standard.

Fondamentalement, l'application ne redémarre pas réellement complètement, mais votre activité de lancement est démarrée et ajoutée en haut de la pile d'activités lorsque l'application est reprise par le lanceur. Vous pouvez confirmer que c'est le cas en cliquant sur le bouton retour lorsque vous reprenez l'application et que vous voyez l'activité de lancement. Vous devriez ensuite être redirigé vers l'activité que vous vous attendiez à voir lorsque vous avez repris l'application.

La solution de contournement que j'ai choisie de mettre en œuvre pour résoudre ce problème est de vérifier la catégorie Intent.CATEGORY_LAUNCHER et l'action Intent.ACTION_MAIN dans l'intention qui démarre l'activité initiale. Si ces deux indicateurs sont présents et que l'activité n'est pas à la racine de la tâche (ce qui signifie que l'application était déjà en cours d'exécution), alors j'appelle finish() sur l'activité initiale. Cette solution exacte peut ne pas fonctionner pour vous, mais quelque chose de similaire devrait.

Voici ce que je fais dans onCreate() de l'activité initiale/de lancement:

    if (!isTaskRoot()
            && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
            && getIntent().getAction() != null
            && getIntent().getAction().equals(Intent.ACTION_MAIN)) {

        finish();
        return;
    }

64voto

Waterbear Points 918

Cette question est toujours pertinente en 2016. Aujourd'hui, un testeur QA a signalé qu'une de mes applications redémarrait au lieu de reprendre à partir du lanceur par défaut dans Android M.

En réalité, le système ajoutait l'activité lancée à la pile de tâches actuelle, mais pour l'utilisateur, cela semblait comme si un redémarrage s'était produit et qu'il avait perdu son travail. La séquence était la suivante :

  1. Téléchargement depuis le Play Store (ou chargement latéral de l'APK)
  2. Lancement de l'application à partir de la boîte de dialogue du Play Store : l'activité A apparaît [pile de tâches : A]
  3. Navigation vers l'activité B [pile de tâches : A -> B]
  4. Appui sur le bouton 'Accueil'
  5. Lancement de l'application depuis le tiroir des applications : l'activité A apparaît ! [pile de tâches : A -> B -> A] (l'utilisateur peut appuyer sur le bouton 'Retour' pour revenir à l'activité 'B' à partir de là)

Remarque : ce problème ne se manifeste pas pour les APK de débogage déployés via ADB, uniquement dans les APK téléchargés depuis le Play Store ou chargés latéralement. Dans ces derniers cas, l'intent de lancement de l'étape 5 contenait le drapeau Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT, mais pas dans les cas de débogage. Le problème disparaît une fois que l'application a été démarrée à froid à partir du lanceur. Je soupçonne que la tâche est semée avec un Intent mal formé (ou plus précisément, non standard) qui empêche le comportement de lancement correct jusqu'à ce que la tâche soit entièrement effacée.

J'ai essayé divers modes de lancement d'activité, mais ces paramètres s'écartent trop du comportement standard que l'utilisateur pourrait attendre : reprendre la tâche à l'activité B. Consultez la définition du comportement attendu dans le guide des tâches et de la pile arrière, en bas de la page sous 'Démarrer une tâche' :

Un filtre d'intent de ce type provoque l'affichage d'une icône et d'une étiquette pour l'activité dans le lanceur d'applications, offrant aux utilisateurs un moyen de lancer l'activité et de revenir à la tâche qu'elle crée à tout moment après son lancement.

J'ai trouvé cette réponse pertinente et j'ai inséré ce qui suit dans la méthode 'onCreate' de mon activité racine (A) afin qu'elle reprenne correctement lorsque l'utilisateur ouvre l'application.

                    /**
     * Assurer la reprise de l'application quelle que soit la tâche que l'utilisateur effectuait la dernière fois
     * qu'il a ouvert l'application à partir du lanceur. Il serait préférable de configurer ce
     * comportement dans les paramètres d'activité du fichier AndroidMananifest.xml, mais ces paramètres entraînent des changements indésirables drastiques
     * dans la façon dont l'application s'ouvre : singleTask ferme TOUS les autres activités
     * dans la tâche à chaque fois et alwaysRetainTaskState ne couvre pas ce cas, incroyablement.
     *
     * Le problème se produit lorsque l'utilisateur installe et ouvre l'application pour la première fois
     * à partir du Play Store ou de l'APK chargé latéralement (pas via ADB). Lors de cette première exécution,
     * si l'utilisateur ouvre l'activité B à partir de l'activité A, appuie sur 'Accueil' puis navigue de nouveau vers l'application via le
     * lanceur, il s'attendrait à voir l'activité B. Au lieu de cela, il voit l'activité A.
     *
     * La meilleure solution est de fermer cette activité si ce n'est pas la racine de la tâche.
     *
     */

    if (!isTaskRoot()) {
        finish();
        return;
    }

MISE À JOUR : j'ai éloigné cette solution de l'analyse des drapeaux d'intent pour vérifier si l'activité est directement à la racine de la tâche. Les drapeaux d'intent sont difficiles à prédire et à tester avec toutes les différentes façons qu'il y a d'ouvrir une activité PRINCIPALE (lancement depuis l'accueil, lancement depuis le bouton 'haut', lancement depuis le Play Store, etc.)

22voto

Graeme Points 9167

Aha! (tldr; Voir les déclarations en gras en bas)

J'ai trouvé le problème... Je pense.

Donc, je vais commencer par une supposition. Lorsque vous appuyez sur le lanceur, il démarre soit l'activité par défaut, soit, si une tâche démarrée par un lancement précédent est ouverte, il la ramène au premier plan. Autrement dit - Si à un moment donné de votre navigation, vous créez une nouvelle tâche et que vous terminez l'ancienne, le lanceur ne reprendra plus votre application.

Si cette supposition est vraie, je suis assez sûr que cela devrait être un bug, étant donné que chaque tâche est dans le même processus et est tout aussi valable comme candidat à reprendre que la première créée ?

Mon problème a alors été résolu en supprimant ces indicateurs de quelques Intentions :

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );

Alors qu'il est assez évident que FLAG_ACTIVITY_NEW_TASK crée une nouvelle tâche, je n'avais pas réalisé que la supposition ci-dessus était en cours. J'avais envisagé cela comme coupable et je l'avais supprimé pour tester et j'avais toujours un problème donc je l'avais rejeté. Cependant, j'avais toujours les conditions suivantes :

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

Mon écran de démarrage lançait l'activité "principale" dans mon application en utilisant le drapeau ci-dessus. Après tout, si je devais "redémarrer" mon application et que l'activité était toujours en cours d'exécution, je préférerais conserver ses informations d'état.

Vous remarquerez dans la documentation qu'il n'est pas mention de démarrer une nouvelle tâche :

Si défini, et que l'activité à lancer est déjà en cours d'exécution dans la tâche actuelle, alors au lieu de lancer une nouvelle instance de cette activité, toutes les autres activités au-dessus d'elle seront fermées et cet Intent sera délivré à l'ancienne activité (maintenant en haut) comme un nouvel Intent.

Par exemple, considérez une tâche composée des activités : A, B, C, D. Si D appelle startActivity() avec un Intent qui résout vers le composant de l'activité B, alors C et D seront terminées et B recevra l'Intent donné, résultant dans la pile maintenant comme ceci : A, B.

L'instance actuellement en cours d'exécution de l'activité B dans l'exemple ci-dessus recevra soit le nouvel intent que vous démarrez ici dans sa méthode onNewIntent(), soit elle-même sera terminée et redémarrée avec le nouvel intent. Si elle a déclaré son mode de lancement comme "multiple" (le par défaut) et que vous n'avez pas défini FLAG_ACTIVITY_SINGLE_TOP dans le même intent, alors elle sera terminée et recréée ; pour tous les autres modes de lancement ou si FLAG_ACTIVITY_SINGLE_TOP est défini alors cet Intent sera délivré à onNewIntent() de l'instance en cours.

Ce mode de lancement peut aussi être utilisé à bon escient en conjonction avec FLAG_ACTIVITY_NEW_TASK : s'il est utilisé pour démarrer l'activité racine d'une tâche, il ramènera n'importe quelle instance actuellement en cours de cette tâche au premier plan, puis l'effacera jusqu'à son état racine. C'est particulièrement utile, par exemple, lors du lancement d'une activité à partir du gestionnaire de notifications.

Donc, j'avais la situation comme décrite ci-dessous :

  • A a lancé B avec FLAG_ACTIVITY_CLEAR_TOP, A se termine.
  • B souhaite redémarrer un service donc envoie l'utilisateur à A qui a la logique de redémarrage du service et l'interface utilisateur (sans drapeaux).
  • A lance B avec FLAG_ACTIVITY_CLEAR_TOP, A se termine.

À ce stade, le deuxième drapeau FLAG_ACTIVITY_CLEAR_TOP redémarre B qui est dans la pile de tâches. Je suppose que cela doit détruire la tâche et en démarrer une nouvelle, provoquant mon problème, ce qui est une situation très difficile à repérer si vous me le demandez !

Donc, si toutes mes suppositions sont correctes :

  • Le Lanceur ne reprend que la Tâche initialement créée
  • FLAG_ACTIVITY_CLEAR_TOP va, s'il redémarre la seule Activité restante, également recréer une nouvelle Tâche

18voto

Ali Points 294

J'ai eu le même problème sur les appareils Samsung. Après avoir beaucoup cherché, aucune de ces réponses n'a fonctionné pour moi. J'ai découvert que dans le fichier AndroidManifest.xml, launchMode est défini sur singleInstance (android:launchMode="singleInstance"). Supprimer l'attribut launchMode a résolu mon problème.

6voto

ptp Points 69

Sur mon Cat s60, j'avais activé "Ne pas conserver les activités" dans les options pour les développeurs, en le désactivant à nouveau cela m'a permis de passer d'une application à l'autre sans perdre l'état des applications...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X