web-dev-qa-db-fra.com

Android: bug dans launchMode = "singleTask"? -> pile d'activités non conservée

Mon activité principale A a pour ensemble Android:launchMode="singleTask" dans le manifeste. Maintenant, chaque fois que je commence une autre activité à partir de là, par exemple. B et appuyez sur le HOME BUTTON du téléphone pour revenir à l'écran d'accueil, puis revenez à mon application, soit en appuyant sur le bouton de l'application, soit en appuyant sur HOME BUTTON pour afficher mes applications les plus récentes. à A au lieu de l'activité attendue B.

Voici les deux comportements:

Expected: A > B > HOME > B
Actual: A > B > HOME > A (bad!)

Y at-il un paramètre qui me manque ou est-ce un bug? Dans ce dernier cas, existe-t-il une solution de contournement jusqu'à ce que le bogue soit corrigé?

FYI: Cette question a déjà été discutée ici . Cependant, il ne semble pas qu'il y ait de réelle solution à ce problème pour le moment.

60
znq

Ce n'est pas un bug. Lorsqu'une activité singleTask existante est lancée, toutes les autres activités situées au-dessus de celle-ci dans la pile sont détruites. 

Lorsque vous appuyez sur HOME et relancez l’activité, ActivityManger appelle une intention.

{act=Android.intent.action.MAIN cat=[Android.intent.category.LAUNCHER]flag=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_RESET_IF_NEEDED cmp=A}

Donc, le résultat est A> B> HOME> A.

C'est différent quand launchMode de A est "Standard". La tâche qui contient A apparaîtra au premier plan et conservera l’état identique à celui d’avant.

Vous pouvez créer une activité "Standard", par exemple. C en tant que lanceur et startActivity (A) dans la méthode onCreate de C

OU

Supprimez simplement le launchMode="singleTask" et définissez le drapeau FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP chaque fois que vous appelez une intention à A

41
renchenyu

Frome http://developer.Android.com/guide/topics/manifest/activity-element.html on singleTask

Le système crée l'activité à la racine d'une nouvelle tâche et achemine l'intention vers celle-ci. Cependant, si une instance de l'activité existe déjà, le système achemine l'intention vers une instance existante via un appel à sa méthode onNewIntent (), plutôt que d'en créer une nouvelle.

Cela signifie que lorsque les options action.MAIN et category.LAUNCHER ciblent votre application à partir du programme de lancement, le système achemine plutôt l'intention vers l'ActivityA existante au lieu de créer une nouvelle tâche et de définir une nouvelle ActivityA en tant que racine. Il préférerait supprimer toutes les activités situées au-dessus de la tâche existante dans laquelle ActivityA réside et invoquer son onNewIntent ().

Si vous voulez capturer à la fois le comportement de singleTop et de singleTask, créez une activité "déléguée" distincte nommée SingleTaskActivity avec le singleTask launchMode qui appelle simplement l'activité singleTop dans sa onCreate (), puis se termine. L'activité singleTop aurait toujours les filtres d'intention MAIN/LAUNCHER pour continuer à jouer le rôle d'activité principale du programme de lancement de l'application, mais lorsque d'autres activités souhaitent appeler cette activité singleTop, elles doivent au contraire invoquer SingleTaskActivity afin de préserver le comportement singleTask. L'intention transmise à l'activité singleTop doit également être reportée à l'activité singleTop. Par conséquent, un des éléments suivants a fonctionné pour moi puisque je souhaitais disposer des modes de lancement singleTask et singleTop.

<activity Android:name=".activities.SingleTaskActivity"
              Android:launchMode="singleTask">

public class SingleTaskActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        intent.setClass(this, SingleTop.class);
        startActivity(intent);
    }
}

Et votre activité singleTop continuerait à avoir son mode de lancement singleTop.

    <activity
        Android:name=".activities.SingleTopActivity"
        Android:launchMode="singleTop"/>

Bonne chance.

5
Ryan

Stefan, tu as déjà trouvé une réponse à ça? J'ai mis en place un cas de test pour cela et je vois le même comportement (perplexe) ... Je vais coller le code ci-dessous au cas où quelqu'un verrait quelque chose d'évident:

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
          package="com.example" >

  <uses-sdk Android:minSdkVersion="3"/>

  <application Android:icon="@drawable/icon" Android:label="testSingleTask">

    <activity Android:name=".ActivityA"
              Android:launchMode="singleTask">
      <intent-filter>
        <action Android:name="Android.intent.action.MAIN"/>
        <category Android:name="Android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>

    <activity Android:name=".ActivityB"/>

  </application>
</manifest>

ActivitéA.Java:

public class ActivityA extends Activity implements View.OnClickListener
{
  @Override
  public void onCreate( Bundle savedInstanceState )
  {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.main );
    View button = findViewById( R.id.tacos );
    button.setOnClickListener( this );
  }

  public void onClick( View view )
  {
    //Intent i = new Intent( this, ActivityB.class );
    Intent i = new Intent();
    i.setComponent( new ComponentName( this, ActivityB.class ) );
    startActivity( i );
  }
}

ActivitéB.Java:

public class ActivityB extends Activity
{
  @Override
  public void onCreate( Bundle savedInstanceState )
  {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.layout_b );
  }
}

J'ai essayé de changer minSdkVersion en vain. Cela semble juste être un bogue, du moins selon le documentation , qui dit ce qui suit:

Comme indiqué ci-dessus, il n'y a jamais plus d'une instance d'une activité "singleTask" ou "singleInstance", de sorte que cette instance est censée gérer toutes les nouvelles intentions. Une activité "singleInstance" est toujours au sommet de la pile (puisqu'il s'agit de la seule activité de la tâche), elle est donc toujours en position de gérer l'intention. Cependant, une activité "tâche unique" peut avoir ou non d'autres activités au-dessus de celle-ci dans la pile. Si c'est le cas, il n'est pas en mesure de gérer l'intention, et l'intention est supprimée. (Même si l'intention est abandonnée, son arrivée aurait amené la tâche à apparaître au premier plan, là où elle resterait.) 

5
Eric

Je pense que c'est le comportement que vous voulez:

singleTask réinitialise la pile sur la presse personnelle pour une raison retardée que je ne comprends pas ... La solution consiste plutôt à ne pas utiliser singleTask et à utiliser standard ou singleTop pour l'activité du lanceur (j'ai essayé seulement avec singleTop à ce jour).

Parce que les applications ont une affinité réciproque, lancez une activité comme celle-ci:

Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
if(launchIntent!=null) {
    launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}

votre pile d’activités va réapparaître comme elle était, sans qu’elle commence une nouvelle activité sur l’ancienne (ce qui était mon principal problème auparavant). Les drapeaux sont les plus importants :

FLAG_ACTIVITY_NEW_TASK Ajouté dans l'API niveau 1

Si elle est définie, cette activité deviendra le début d’une nouvelle tâche à ce sujet pile de l'histoire. Une tâche (de l'activité qui l'a démarrée à l'activité de tâche Suivante) définit un groupe atomique d'activités que l'utilisateur peut utiliser déménager à. Les tâches peuvent être déplacées au premier plan et à l'arrière-plan; tous les activités à l'intérieur d'une tâche particulière restent toujours dans la même chose ordre. Voir Tâches et pile d'arrière-plan pour plus d'informations sur les tâches.

Ce drapeau est généralement utilisé par les activités qui souhaitent présenter un Comportement de style "lanceur": ils donnent à l'utilisateur une liste de fichiers séparés des choses qui peuvent être faites, qui fonctionnent autrement complètement indépendamment de l'activité qui les lance.

Lors de l'utilisation de cet indicateur, si une tâche est déjà en cours d'exécution pour l'activité vous commencez maintenant, alors une nouvelle activité ne sera pas commencée; au lieu de cela, la tâche en cours sera simplement portée au début du fichier écran avec l'état dans lequel il se trouvait en dernier. Voir FLAG_ACTIVITY_MULTIPLE_TASK pour un drapeau pour désactiver ce comportement.

Cet indicateur ne peut pas être utilisé lorsque l'appelant demande un résultat à l'activité en cours de lancement.

Et:

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Ajouté au niveau 1 de l'API

Si défini, et que cette activité est en cours de démarrage dans une nouvelle tâche ou en mettant en haut une tâche existante, elle sera lancée en tant que porte d'entrée de la tâche. Cela se traduira par l'application de tout les affinités devaient avoir cette tâche dans l’état approprié (soit en y déplaçant des activités ), soit en réinitialisant simplement cette tâche. état initial si nécessaire.

Sans eux, l'activité lancée sera simplement poussée au-dessus de l'ancienne pile ou de tout autre comportement indésirable (dans ce cas bien sûr)

Je crois que le problème de ne pas recevoir la dernière intention peut être résolu comme ceci (hors de ma tête):

@Override
public void onActivityReenter (int resultCode, Intent data) {
    onNewIntent(data);
}

Essaye le!

2
JohnyTex

Si les deux A et B appartiennent à la même Application, essayez de supprimer

Android:launchMode="singleTask"

de votre Activities et test parce que je pense que le comportement par défaut est ce que vous avez décrit comme prévu.

1

C'est ainsi que j'ai finalement résolu ce comportement étrange. Dans AndroidManifest, voici ce que j'ai ajouté:

Application & Root activity
            Android:launchMode="singleTop"
            Android:alwaysRetainTaskState="true"
            Android:taskAffinity="<name of package>"

Child Activity
            Android:parentActivityName=".<name of parent activity>"
            Android:taskAffinity="<name of package>"
0
Carlos Hoyos

Ajouter ci-dessous dans l'activité manifeste d'Android, cela ajoutera une nouvelle tâche en haut de la vue en détruisant les tâches précédentes . Android: launchMode = "singleTop" comme ci-dessous

 <activity
        Android:name=".MainActivity"
        Android:label="@string/app_name"
        Android:launchMode="singleTop"
        Android:theme="@style/AppTheme.NoActionBar">
    </activity>
0
Rockit Rockit

Lorsque vous utilisez le mode de lancement en tant que singleTop, appelez finish () (pour l'activité en cours, indiquez A) lors du démarrage de l'activité suivante (à l'aide de la méthode startActivity (Intent), indiquez B). De cette façon, l'activité en cours est détruite. A -> B -> Suspendre l'application et cliquer sur l'icône du lanceur, démarre A Dans la méthode de création de A, vous devez cocher une case,

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

Ainsi, lors du lancement de l'application, nous vérifions la tâche racine et la tâche précédemment racine est B mais pas A. Cette vérification détruit donc l'activité A et nous conduit à l'activité B qui se trouve actuellement en haut de la pile. J'espère que ça marche pour toi!.

0
Venkat Saladi
        <activity Android:name=".MainActivity"
         Android:launchMode="singleTop">
         <intent-filter>
            <action Android:name="Android.intent.action.MAIN" />
            <category Android:name="Android.intent.category.LAUNCHER" />
         </intent-filter>
       </activity>

// Essayez d'utiliser launchMode = "singleTop" dans votre activité principale pour gérer une instance unique de votre application. Aller au manifeste et changer. 

0
Jay

Chaque fois que vous appuyez sur le bouton d'accueil pour revenir à votre écran d'accueil, la pile d'activités tue certaines des applications lancées et en cours d'exécution précédemment.

Pour vérifier ce fait, essayez de lancer une application à partir du panneau de notification après être passé de A à B dans votre application et de revenir en utilisant le bouton Précédent .......... vous trouverez votre application dans le même état que vous l'a laissé.

0
Siddhartho