129 votes

Comment empêcher les instances multiples d'une activité lorsqu'elle est lancée avec des intentions différentes ?

J'ai rencontré un bug dans mon application lorsqu'elle est lancée à l'aide de la fonction "Ouvert" sur l'application Google Play Store (anciennement appelée Android Market). Il semble que le lancement à partir du Play Store utilise une méthode différente de celle utilisée pour le lancement de l'application. Intent plutôt que de la lancer à partir du menu d'icônes des applications du téléphone. Cela conduit au lancement de plusieurs copies de la même activité, qui entrent en conflit les unes avec les autres.

Par exemple, si mon application est constituée des activités A-B-C, alors ce problème peut conduire à une pile de A-B-C-A.

J'ai essayé d'utiliser android:launchMode="singleTask" sur toutes les activités pour résoudre ce problème, mais cela a l'effet secondaire indésirable de vider la pile d'activités à la racine, chaque fois que j'appuie sur le bouton HOME.

Le comportement attendu est le suivant : A-B-C -> HOME -> Et quand l'application est restaurée, j'ai besoin : A-B-C -> MAISON -> A-B-C

Existe-t-il un bon moyen d'empêcher le lancement de plusieurs activités du même type, sans revenir à l'activité racine lorsque l'on utilise le bouton HOME ?

0 votes

197voto

Duane Homick Points 851

Ajoutez ceci à onCreate et vous devriez être prêt à partir :

// Possible work around for market launches. See https://issuetracker.google.com/issues/36907463
// for more details. Essentially, the market launches the main activity on top of other activities.
// we never want this to happen. Instead, we check if we are the root and if not, we finish.
if (!isTaskRoot()) {
    final Intent intent = getIntent();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(intent.getAction())) {
        Log.w(LOG_TAG, "Main Activity is not the root.  Finishing Main Activity instead of launching.");
        finish();
        return;       
    }
}

26 votes

Cela fait des années que j'essaie de résoudre ce bug, et c'est la solution qui a fonctionné, alors merci beaucoup ! Je dois également noter que ce n'est pas seulement un problème dans l'Android Market, mais aussi sideloader une application en la téléchargeant sur un serveur, ou en l'envoyant par e-mail à votre téléphone, cause ce problème. Toutes ces choses installent l'application en utilisant le Package Installer, où je pense que le bug réside. En outre, au cas où ce ne serait pas clair, il vous suffit d'ajouter ce code à la méthode onCreate de votre activité racine.

2 votes

Je trouve très étrange que cela se produise dans une application signée déployée sur l'appareil, mais pas dans une version de débogage déployée depuis Eclipse. Cela rend le débogage assez difficile !

6 votes

Ce site fait se produisent avec une version de débogage déployée à partir d'Eclipse pour autant que vous la STARTiez également via Eclipse (ou IntelliJ ou autre IDE). Cela n'a rien à voir avec la façon dont l'application est déployée. installé sur l'appareil. Le problème est dû à la façon dont l'application est utilisée. a commencé .

29voto

gilm Points 1208

Je vais simplement expliquer pourquoi cela échoue, et comment reproduire ce bogue de manière programmatique afin que vous puissiez l'intégrer dans votre suite de tests :

  1. Lorsque vous lancez une application via Eclipse ou Market App, elle se lance avec des drapeaux d'intention : FLAG_ACTIVITY_NEW_TASK.

  2. Lors d'un lancement via le lanceur (home), il utilise des drapeaux : FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, et utilise l'action " MAIN "et catégorie " LAUNCHER ".

Si vous souhaitez reproduire ce phénomène dans un scénario de test, procédez comme suit :

adb shell am start -f 0x10000000 -n com.testfairy.tests.regression.taskroot/.MainActivity 

Ensuite, faites ce qui est nécessaire pour passer à l'autre activité. Pour mes besoins, j'ai juste placé un bouton qui démarre une autre activité. Ensuite, retournez au lanceur (maison) avec :

adb shell am start -W -c android.intent.category.HOME -a android.intent.action.MAIN

Et simulez son lancement via le lanceur avec ceci :

adb shell am start -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -f 0x10600000 -n com.testfairy.tests.regression.taskroot/.MainActivity

Si vous n'avez pas incorporé la solution de contournement isTaskRoot(), cela reproduira le problème. Nous l'utilisons dans nos tests automatiques pour nous assurer que ce bug ne se reproduira plus jamais.

J'espère que cela vous aidera !

10voto

elevine Points 7208

Avez-vous essayé le singleTop mode de lancement ?

Voici une partie de la description de http://developer.Android.com/guide/topics/manifest/activity-element.html :

... une nouvelle instance de l'activité "singleTop". peut également être créée pour gérer une nouvelle intention. Cependant, si la tâche cible cible possède déjà une instance existante de l'activité au sommet de sa pile pile, cette instance recevra la nouvelle intention (dans un appel onNewIntent()) ; une nouvelle instance n'est pas créée. Dans d'autres circonstances - par exemple, si une instance existante de l'activité l'activité "singleTop" se trouve dans la tâche cible, mais pas au sommet de la pile, ou si elle est au sommet d'une pile, mais mais pas dans la tâche cible, une nouvelle instance sera créée et poussée sur la pile.

2 votes

J'y ai pensé, mais que faire si l'activité n'est pas au sommet de la pile ? Par exemple, il semble que singleTop empêchera A-A, mais pas A-B-A.

0 votes

Pouvez-vous obtenir ce que vous voulez en utilisant singleTop et les méthodes de finition dans Activity ?

0 votes

Je ne sais pas si cela permettra d'obtenir ce que je veux. Exemple : Si je suis sur l'activité C après avoir lancé A et B, puis une nouvelle activité A est lancée et j'aurai quelque chose comme C-A, n'est-ce pas ?

4voto

DuneCat Points 476

Peut-être que c'est cette question ? Ou une autre forme du même bug ?

0 votes

Voir aussi code.google.com/p/Android/issues/detail?id=26658 ce qui démontre qu'il est causé par d'autres choses qu'Eclipse.

1 votes

Je devrais donc copier-coller une description de problème qui risque de devenir obsolète ? Quelles parties ? Les parties essentielles doivent-elles être conservées si le lien change, et est-il de ma responsabilité que la réponse soit maintenue à jour ? Il faut penser que le lien ne devient caduc que si le problème est résolu. Il ne s'agit pas d'un lien vers un blog, après tout.

0voto

gugarush Points 32

J'ai eu le même problème, et je l'ai résolu en utilisant la solution suivante.

Dans votre activité principale, ajoutez le code suivant en haut de l'activité onCreate méthode :

ActivityManager manager = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE );
List<RunningTaskInfo> tasks =  manager.getRunningTasks(Integer.MAX_VALUE);

for (RunningTaskInfo taskInfo : tasks) {
    if(taskInfo.baseActivity.getClassName().equals(<your package name>.<your class name>) && (taskInfo.numActivities > 1)){
        finish();
    }
}

n'oubliez pas d'ajouter cette permission dans votre manifeste.

< uses-permission android:name="android.permission.GET_TASKS" />

J'espère que cela vous aidera.

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