47 votes

Mode de lancement "single top" d'Android et méthode onNewIntent

J'ai lu dans la documentation d'Android qu'en définissant la propriété launchMode de mon activité sur singleTop OU en ajoutant la balise FLAG_ACTIVITY_SINGLE_TOP drapeau à mon intention, que l'appel startActivity(intent) réutiliserait une seule instance de l'activité et me donnerait l'intention dans la onNewIntent rappel. J'ai fait ces deux choses, et onNewIntent jamais de feu et onCreate fait feu à chaque fois. Les documents disent aussi que this.getIntent() renvoie l'intention qui a été transmise à l'activité lors de sa création. Dans onCreate J'appelle getIntent et j'en reçois un nouveau à chaque fois (je crée l'objet intentionnel dans une autre activité et j'y ajoute un extra... cet extra devrait être le même à chaque fois s'il me renvoie le même objet intentionnel). Tout cela me porte à croire que mon activité ne se comporte pas comme un "sommet unique", et je ne comprends pas pourquoi.

Pour ajouter un peu de contexte au cas où je manquerais simplement une étape nécessaire, voici ma déclaration d'activité dans le manifeste et le code que j'utilise pour lancer l'activité. L'activité elle-même ne fait rien de particulier à ce sujet :

dans AndroidManifest.xml :

    <activity
        android:name=".ArtistActivity"
        android:label="Artist"
        android:launchMode="singleTop">
    </activity>     

dans mon activité d'appel :

        Intent i = new Intent();
        i.putExtra(EXTRA_KEY_ARTIST, id);
        i.setClass(this, ArtistActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(i);

2 votes

Regardez ce diaporama. Il explique très bien launchMode : slideshare.net/RanNachmany/

38voto

znq Points 13027

Avez-vous vérifié si onDestroy() a été appelé aussi ? C'est probablement pourquoi onCreate() est invoqué à chaque fois au lieu de onNewIntent() qui ne sera appelé que si l'activité existe déjà.

Par exemple, si vous quittez votre activité en appuyant sur le bouton BACK, elle est détruite par défaut. Mais si vous montez plus haut dans la pile d'activités, dans d'autres activités, et que vous appelez votre activité de la manière suivante ArtistActivity.class encore une fois, il sautera onCreate() et aller directement à onNewIntent() parce que l'activité a déjà été créée et que vous l'avez définie en tant que singleTop Android ne créera pas une nouvelle instance, mais prendra celle qui existe déjà.

Ce que je fais pour voir ce qui se passe, c'est que j'implémente des fonctions factices pour tous les différents états de chaque activité, de sorte que je sais toujours ce qui se passe :

@Override
public void onDestroy() {
    Log.i(TAG, "onDestroy()");
    super.onDestroy();
}

Idem pour onRestart() , onStart() , onResume() , onPause() , onDestroy()

Si ce qui précède (bouton BACK) n'était pas votre problème, la mise en œuvre de ces astuces vous aidera au moins à déboguer un peu mieux.

0 votes

C'est très probablement ce qui se passe. J'ai créé quelque chose comme une activité de "page d'atterrissage" comme point d'entrée. À partir du point d'atterrissage, vous sélectionnez un artiste et accédez à l'activité ArtistActivity. L'utilisateur utilise le bouton retour pour revenir à l'activité principale et sélectionner à nouveau. J'avais l'impression que l'activité resterait en place après la première utilisation et que je gagnerais en performance en ne la réinstaurant pas. Dois-je m'inquiéter des performances dans ce cas, ou ce modèle convient-il aux activités simples ?

2 votes

Bonjour, j'ai le même problème que OP, mais onDestroy n'est jamais appelé dans mon cas - je me retrouve avec de toutes nouvelles instances de l'activité :-s. Une autre idée ?

17 votes

Android:launchMode="singleTask" - dans mon cas, l'activité en question a été lancée en dehors de sa tâche principale.

34voto

Steve B Points 41

La réponse acceptée n'est pas tout à fait correcte. Si onDestroy() a été appelé précédemment, alors oui, onCreate() sera toujours appelé. Cependant, cette déclaration est fausse : "Si vous montez plus haut sur la pile d'activités dans d'autres activités et que de là, vous appelez à nouveau votre ArtistActivity.class, il sautera onCreate() et ira directement à onNewIntent()."

La section "singleTop" de http://developer.Android.com/guide/components/tasks-and-back-stack.html explique clairement comment cela fonctionne (attention au texte en gras ci-dessous ; j'ai également prouvé cela par mon propre débogage) :

"Par exemple, supposons que la pile arrière d'une tâche se compose de l'activité A de la racine avec les activités B, C et D par-dessus ( la pile est A-B-C-D ; D est sur le dessus ). Une intention arrive pour une activité de type D. Si D possède le 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'intention par le biais de onNewIntent(), car elle se trouve au sommet de la pile - la pile reste A-B-C-D. Cependant, si une intention arrive pour une activité de type B, alors une nouvelle instance de B est ajoutée à la pile, même si son mode de lancement est "singleTop"".

En d'autres termes, le démarrage d'une activité par SINGLE_TOP ne réutilisera l'activité existante que si elle est déjà au sommet de la pile . Cela ne fonctionnera pas si une autre activité de cette même tâche se trouve au sommet (par exemple, l'activité qui exécute startActivity(SINGLE_TOP)) ; une nouvelle instance sera créée à la place.

Voici deux façons de résoudre ce problème afin d'obtenir le comportement SINGLE_TOP que vous souhaitez -- dont le but général est de réutiliser une activité existante, au lieu d'en créer une nouvelle...

Premier moyen (tel qu'il est décrit dans la section des commentaires de la demande acceptée). réponse acceptée) : Vous pourriez ajouter un launchMode de "singleTask" à votre activité. Cela forcerait onNewIntent() parce que singleTask signifie qu'il ne peut y avoir qu'UNE seule instance d'une activité particulière dans une tâche donnée. C'est une solution un peu bancale car si votre application a besoin de plusieurs instances de cette activité dans une situation particulière (comme c'est le cas pour mon projet), vous êtes fichu.

Deuxième voie (meilleure) : Au lieu de FLAG_ACTIVITY_SINGLE_TOP, utilisez FLAG_ACTIVITY_REORDER_TO_FRONT. Cela réutilisera l'instance d'activité existante en la déplaçant au sommet de la pile (onNewIntent() sera appelé comme prévu).

L'objectif principal de FLAG_ACTIVITY_SINGLE_TOP est d'empêcher la création d'instances multiples d'une activité. Par exemple, lorsque cette activité peut être lancée via une intention qui provient de l'extérieur de la tâche principale de votre application. Pour le passage interne entre les activités dans mon application, j'ai trouvé que FLAG_ACTIVITY_REORDER_TO_FRONT est généralement ce que je veux à la place.

3voto

Kodak Points 67

Réglez ce drapeau sur votre intention :

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)

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