Effacez toute la pile d'historique et lancez une nouvelle activité sur Android

332

Est-il possible de démarrer une activité sur la pile, en effaçant tout l'historique avant elle?

La situation

J'ai une pile d'activités qui va soit A-> B-> C ou B-> C (l'écran A sélectionne le jeton des utilisateurs, mais de nombreux utilisateurs n'ont qu'un seul jeton).

Dans l'écran C, l'utilisateur peut effectuer une action qui rend l'écran B invalide, de sorte que l'application souhaite les amener à l'écran A, qu'il soit déjà dans la pile ou non. L'écran A devrait alors être le seul élément de la pile dans mon application.

Remarques

Il existe de nombreuses autres questions similaires, mais je n'ai rien trouvé qui réponde à cette question exacte. J'ai essayé d'appeler getParent().finish()- cela entraîne toujours une exception de pointeur nul. FLAG_ACTIVITY_CLEAR_TOPne fonctionne que si l'activité est déjà sur la pile.

Casebash
la source

Réponses:

658

Dans l'API niveau 11, un nouvel indicateur d' intention a été ajouté juste pour cela: Intent.FLAG_ACTIVITY_CLEAR_TASK

Juste pour clarifier, utilisez ceci:

Java

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

Kotlin

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

Malheureusement pour l'API lvl <= 10, je n'ai pas encore trouvé de solution propre à cela. La solution "DontHackAndroidLikeThis" est en effet un pur hackery. Tu ne devrais pas faire ça. :)

Edit: Selon le commentaire de @ Ben Pearson , pour API <= 10, on peut maintenant utiliser la classe IntentCompat pour la même chose. On peut utiliser le IntentCompat.FLAG_ACTIVITY_CLEAR_TASKdrapeau pour effacer la tâche. Vous pouvez donc également prendre en charge la pré API niveau 11.

Akos Cz
la source
23
Juste pour clarifier, utilisez ceci: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
user123321
2
sans Intent.FLAG_ACTIVITY_NEW_TASK, l'application se ferme parfois simplement sur Android 4
max4ever
22
IntentCompat a également un indicateur pour effacer la tâche, vous pouvez donc prendre en charge la pré API niveau 11 - developer.android.com/reference/android/support/v4/content/…
Ben Pearson
10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK est ignoré sur les appareils avec un niveau d'API <10. developer.android.com/reference/android/support/v4/content/…
David
7
Le drapeau d'IntentCompat sert uniquement à éviter un plantage, mais ne fait rien comme le dit @David.
Sloy
49

Cas 1: Seulement deux activités A et B:

Ici, le flux d'activité est A-> B.En cliquant sur le bouton de retour de B, nous devons fermer l'application, puis lors du démarrage de l'activité B à partir de A, appelez simplement finish (), cela empêchera Android de stocker l'activité A dans le Backstack.eg pour l'activité A est Écran d'application de dépôt / Splash.

Intent newIntent = new Intent(A.this, B.class);
startActivity(newIntent);
finish();

Cas 2: Plus de deux activités:

S'il y a un flux comme A-> B-> C-> D-> B et en cliquant sur le bouton de retour dans l'activité B en venant de l'activité D. Dans ce cas, nous devrions utiliser.

Intent newIntent = new Intent(D.this,B.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);

Ici, l'activité B sera démarrée à partir du backstack plutôt que d'une nouvelle instance en raison de Intent.FLAG_ACTIVITY_CLEAR_TOP et Intent.FLAG_ACTIVITY_NEW_TASK efface la pile et en fait la première. Ainsi, lorsque nous appuierons sur le bouton de retour, l'application entière sera terminée.

George monish
la source
2
Cela a fonctionné pour moi. J'ai mis dans TOUTES les activités ces drapeaux. Dans ces activités, les boutons de retour fonctionnent parfaitement en allant à l'activité précédente, et dans l'activité principale avec intention intention = nouvelle intention (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (intention); terminer(); L'application entière est fermée, toujours en mémoire mais pas active, et si vous redémarrez l'application va à l'écran de démarrage :)
Rako
Ce devrait être la meilleure réponse. Si quelqu'un a un scénario identique avec moi: A-> B-> C-> D-> E -> (B) De E-> B devrait avoir un résultat: A-> B
Shem Alexis Chavez
39

Avec la nouvelle version d'Android> = utilisation de l'API 16 finishAffinity()

convient à> = API 16.

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • C'est la même chose que de commencer une nouvelle activité et de vider toute la pile.
  • OU Redémarrez vers MainActivity / FirstActivity.
Karan
la source
1
Cela a fait l'affaire, les drapeaux ne fonctionnaient pas sur 4.xx pour moi et cela a parfaitement fonctionné! Merci
Jonathan Aste
1
Cela semble être la bonne réponse si votre objectif est de terminer toutes les activités ci-dessous et d'inclure l'activité en cours et de commencer une nouvelle activité dans leur propre tâche.
ToBe
24

J'ai aussi passé quelques heures là-dessus ... et je suis d'accord pour dire que FLAG_ACTIVITY_CLEAR_TOP ressemble à ce que vous voudriez: effacer toute la pile, à l'exception de l'activité en cours de lancement, donc le bouton Précédent quitte l'application. Pourtant, comme Mike Repass l'a mentionné, FLAG_ACTIVITY_CLEAR_TOP ne fonctionne que lorsque l'activité que vous lancez est déjà dans la pile; quand l'activité n'est pas là, le drapeau ne fait rien.

Que faire? Placez l'activité en cours de lancement dans la pile avec FLAG_ACTIVITY_NEW_TASK, ce qui fait de cette activité le début d'une nouvelle tâche sur la pile d'historique. Ajoutez ensuite l'indicateur FLAG_ACTIVITY_CLEAR_TOP.

Maintenant, lorsque FLAG_ACTIVITY_CLEAR_TOP va chercher la nouvelle activité dans la pile, elle sera là et sera relevée avant que tout le reste ne soit effacé.

Voici ma fonction de déconnexion; le paramètre View est le bouton auquel la fonction est attachée.

public void onLogoutClick(final View view) {
    Intent i = new Intent(this, Splash.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(i);
    finish();
}
user2895402
la source
1
voulez-vous dire CLEAR_TASK au lieu de CLEAR_TOP?
Andy
14

Vous ne devez pas changer la pile. Le bouton de retour Android devrait fonctionner comme dans un navigateur Web.

Je peux penser à un moyen de le faire, mais c'est tout un hack.

  • Faites vos activités singleTasken l'ajoutant à l' AndroidManifest exemple:

    <activity android:name=".activities.A"
              android:label="@string/A_title"
              android:launchMode="singleTask"/>
    
    <activity android:name=".activities.B"
              android:label="@string/B_title"
              android:launchMode="singleTask"/>
  • Prolongez Applicationce qui détiendra la logique de l'endroit où aller.

Exemple:

public class DontHackAndroidLikeThis extends Application {

  private Stack<Activity> classes = new Stack<Activity>();

  public Activity getBackActivity() {
    return classes.pop();
  }

  public void addBackActivity(Activity activity) {
    classes.push(activity);
  }
}

De A à B:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(A.class); 
startActivity(this, B.class);

De B ​​à C:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(B.class); 
startActivity(this, C.class);

En C:

If ( shouldNotGoBackToB() ) {
  DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
  app.pop();
}

et manipulez le bouton de retour à pop()partir de la pile.

Encore une fois, vous ne devriez pas faire ça :)

Macarse
la source
À la fin, je décide de laisser la pile intacte et de dire simplement à l'utilisateur que son écran actuel n'était pas valide
Casebash
1
Très frustrant que Android ne nous laisse pas déjà gérer la pile d'activités de cette façon. Je serais tenté d'utiliser cette solution dans mes futures applications Android.
Cephron
4
Juste pour être clair pourquoi cela ne devrait pas être utilisé: c'est un bon moyen de créer des fuites de mémoire. À un moment donné, le système d'exploitation peut décider de tuer les activités d'arrière-plan, mais comme il Applicationprend leurs instances, le système d'exploitation ne sera pas en mesure de libérer la RAM restante des activités détruites.
Vit Khudenko
@Arhimed Y a-t-il d'autres problèmes? La fuite de mémoire peut être corrigée en ne gardant que des références faibles.
Navin
1
@Navin oui, les fuites peuvent être évitées avec des références faibles, mais si après GC, il n'y aura pas de référence d'activité en direct, alors l'approche entière est inutile. Encore une fois - ne faites pas cela, c'est une mauvaise approche pour Android.
Vit Khudenko du
12

Immédiatement après avoir démarré une nouvelle activité, à l'aide de startActivity, assurez-vous d'appeler finish()afin que l'activité en cours ne soit pas empilée derrière la nouvelle.

Keith Maurino
la source
+1 Belle solution pour empêcher exactement une activité dans une certaine situation d'être placée dans la pile d'historique.
marsbear
27
ne fonctionne pas si vous avez plus d'une activité dans la pile, l'arrivée
effacera
5

Essaye ça:

Intent logout_intent = new Intent(DashboardActivity.this, LoginActivity.class);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(logout_intent);
finish();
Mohammad
la source
4

Kotlin réutilisable avancé:

Vous pouvez définir le drapeau directement en utilisant la méthode setter. Dans Kotlin orest le remplacement de Java au niveau du bit ou |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Si vous prévoyez de l'utiliser régulièrement, créez une fonction d'extension d'intention

fun Intent.clearStack() {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

Vous pouvez ensuite appeler directement cette fonction avant de démarrer l'intention

intent.clearStack()

Si vous avez besoin d'ajouter des indicateurs supplémentaires dans d'autres situations, ajoutez un paramètre facultatif à la fonction d'extension.

fun Intent.clearStack(additionalFlags: Int = 0) {
    flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
Gibolt
la source
2
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
Neeraj Gupta
la source
2

Essayez le code ci-dessous,

Intent intent = new Intent(ManageProfileActivity.this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
                Intent.FLAG_ACTIVITY_CLEAR_TASK| 
                Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
shashikant yadav
la source
si j'utilise comme cette activité est mis à jour à nouveau appel api mais précédemment existant tout statck est effacé
Harsha
2

Pour moi, aucune des méthodes ci-dessus ne fonctionne.

Faites simplement ceci pour effacer toutes les activités précédentes :

finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)
Amir Hossein Ghasemi
la source
-1

Parfois, votre émulateur Android peut ne pas connecter l'outil DDMS eclipse et demander à adb de démarrer manuellement. Dans ce cas, vous pouvez démarrer ou arrêter l'adb à l'aide de l'invite de commande.

RajeshkumarG
la source
1
Parfois, votre émulateur Android peut ne pas connecter l'outil DDMS eclipse et demander à adb de démarrer manuellement. Dans ce cas, vous pouvez démarrer ou arrêter l'adb à l'aide de l'invite de commande. Intention i = nouvelle intention (OldActivity.this, NewActivity.class); // définit la nouvelle tâche et efface les indicateurs i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
RajeshkumarG
-2

J'ai trouvé un hack trop simple, il suffit d'ajouter ceci AndroidManifestcomme: -

<activity android:name=".activityName"
          android:label="@string/app_name"
          android:noHistory="true"/>

le android:noHistoryva effacer votre activité indésirable de la pile.

Tauseef
la source
2
Ce problème peut provoquer des problèmes sur Android 6.0+, si vous demandez des autorisations dans cette activité.
Vitaliy A