Cancella l'intero stack della cronologia e inizia una nuova attività su Android


332

È possibile avviare un'attività in pila, cancellando l'intera cronologia prima di essa?

La situazione

Ho uno stack di attività che va A-> B-> C o B-> C (la schermata A seleziona il token degli utenti, ma molti utenti hanno solo un singolo token).

Nella schermata C l'utente può eseguire un'azione che rende non valida la schermata B, quindi l'applicazione desidera portarli alla schermata A, indipendentemente dal fatto che sia già nello stack. La schermata A dovrebbe quindi essere l'unico elemento nello stack nella mia applicazione.

Appunti

Ci sono molte altre domande simili, ma non ho trovato nulla che risponda a questa domanda esatta. Ho provato a chiamare getParent().finish()- questo si traduce sempre in un'eccezione puntatore null. FLAG_ACTIVITY_CLEAR_TOPfunziona solo se l'attività è già in pila.

Risposte:


658

Nel livello API 11 è stato aggiunto un nuovo Flag di intenti proprio per questo: Intent.FLAG_ACTIVITY_CLEAR_TASK

Solo per chiarire, usa questo:

Giava

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

Sfortunatamente per API lvl <= 10, non ho ancora trovato una soluzione pulita a questo. La soluzione "DontHackAndroidLikeThis" è davvero pura pirateria informatica. Non dovresti farlo. :)

Modifica: Secondo il commento di @ Ben Pearson , per API <= 10 ora è possibile utilizzare la classe IntentCompat per lo stesso. Si può usare il IntentCompat.FLAG_ACTIVITY_CLEAR_TASKflag per cancellare l'attività. Quindi puoi supportare anche il livello pre API 11.


23
Solo per chiarire, usare questo: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
user123321

2
senza Intent.FLAG_ACTIVITY_NEW_TASK l'app a volte si chiude su Android 4
max4ever

22
IntentCompat ha anche un flag per cancellare l'attività ora, quindi puoi supportare pre API livello 11 - developer.android.com/reference/android/support/v4/content/…
Ben Pearson

10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK viene ignorato su dispositivi con livello API <10. developer.android.com/reference/android/support/v4/content/…
David

7
La bandiera di IntentCompat serve solo per evitare un crash, ma non fa nulla come dice @David.
Sloy il

49

Caso 1: solo due attività A e B:

Qui il flusso di attività è A-> B. Facendo clic sul pulsante indietro da B, è necessario chiudere l'applicazione, mentre all'avvio dell'attività B da A basta terminare la chiamata () questo impedirà ad Android di memorizzare l'attività A nel Backstack.eg per l'attività A è Schermata di presentazione / splash dell'applicazione.

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

Caso 2: più di due attività:

Se c'è un flusso come A-> B-> C-> D-> B e facendo clic sul pulsante indietro nell'Attività B mentre proviene dall'Attività D. In tal caso dovremmo usare.

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

Qui l'attività B verrà avviata dal backstack anziché da una nuova istanza a causa di Intent.FLAG_ACTIVITY_CLEAR_TOP e Intent.FLAG_ACTIVITY_NEW_TASK cancella lo stack e lo rende il migliore, quindi quando si preme il pulsante indietro l'intera applicazione verrà chiusa.


2
Questo ha funzionato per me. Ho messo TUTTE le attività in quelle bandiere. In quelle attività i pulsanti indietro funzionano perfettamente andando all'attività precedente e nell'attività principale con intento intento = nuovo intento (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (intento); finire(); L'intera app è chiusa, ancora in memoria ma non attiva, e se riavvii l'app passa alla schermata iniziale :)
Rako

Questa dovrebbe essere la risposta migliore. Se qualcuno ha uno stesso scenario con me: A-> B-> C-> D-> E -> (B) Da E-> B dovrebbe avere un risultato: A-> B
Shem Alexis Chavez,

39

Con la versione più recente di Android> = API 16 utilizzare finishAffinity()

l'approccio è adatto per> = API 16.

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • È come iniziare una nuova attività e cancellare tutto lo stack.
  • OPPURE Riavvia a MainActivity / FirstActivity.

1
Questo ha funzionato, le bandiere non funzionavano su 4.xx per me e ha funzionato perfettamente! Grazie
Jonathan Aste,

1
Questa sembra essere la risposta corretta se il tuo obiettivo è completare tutte le attività sottostanti e includendo l'attività corrente e iniziare una nuova attività nella propria attività.
Dal

24

Ho trascorso alcune ore anche su questo ... e sono d'accordo che FLAG_ACTIVITY_CLEAR_TOP suona come quello che vorresti: cancella l'intero stack, ad eccezione dell'attività avviata, quindi il pulsante Indietro esce dall'applicazione. Tuttavia, come menzionato da Mike Repass, FLAG_ACTIVITY_CLEAR_TOP funziona solo quando l'attività che stai lanciando è già nello stack; quando l'attività non c'è, la bandiera non fa nulla.

Cosa fare? Inserisci l'attività in fase di avvio nello stack con FLAG_ACTIVITY_NEW_TASK, che rende tale attività l'inizio di una nuova attività nello stack della cronologia. Quindi aggiungere il flag FLAG_ACTIVITY_CLEAR_TOP.

Ora, quando FLAG_ACTIVITY_CLEAR_TOP andrà a cercare la nuova attività nello stack, questa sarà lì e verrà tirata su prima che tutto il resto venga cancellato.

Ecco la mia funzione di logout; il parametro Visualizza è il pulsante a cui è collegata la funzione.

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();
}

1
intendi CLEAR_TASK invece di CLEAR_TOP?
Andy,

14

Non dovresti cambiare lo stack. Il pulsante Indietro di Android dovrebbe funzionare come in un browser web.

Mi viene in mente un modo per farlo, ma è piuttosto un trucco.

  • Crea le tue attività singleTaskaggiungendole AndroidManifest all'esempio:

    <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"/>
  • Estendi Applicationche conterrà la logica di dove andare.

Esempio:

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);
  }
}

Da A a B:

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

Dalla B alla C:

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

In C:

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

e gestisci il pulsante Indietro pop()dalla pila.

Ancora una volta, non dovresti farlo :)


Alla fine decido di lasciare intatto lo Stack e dire semplicemente all'utente che la loro schermata attuale non è valida
Casebash

1
Molto frustrante che Android non ci consenta già di gestire lo stack di attività in questo modo. Sarei tentato di utilizzare questa soluzione nelle mie future app Android.
Cephron,

4
Giusto per essere chiari perché questo non dovrebbe essere usato: è un bel modo per creare perdite di memoria. Ad un certo punto il sistema operativo potrebbe decidere di interrompere le attività in background, ma dato che Applicationprende le loro istanze il sistema operativo non sarà in grado di liberare quella RAM lasciata dalle attività distrutte.
Vit Khudenko,

@Arhimed Ci sono altri problemi? La perdita di memoria può essere corretta mantenendo solo i riferimenti deboli.
Navin,

1
@Navin sì, le perdite possono essere evitate con riferimenti deboli, ma se dopo GC non ci sarà un riferimento all'attività attivo, l'intero approccio è inutile. Ancora una volta - non farlo, questo è un approccio sbagliato per Android.
Vit Khudenko,

12

Immediatamente dopo aver avviato una nuova attività, utilizzando startActivity, assicurarsi di chiamare in finish()modo che l'attività corrente non sia sovrapposta a quella nuova.


+1 Bella soluzione per impedire che esattamente un'attività in una determinata situazione venga messa nello stack della cronologia.
Marsbear,

27
non funziona se hai più di un'attività nello stack, il traguardo cancellerà solo l'attività precedente ma non le altre ....
Necronet

5

Prova questo:

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();

4

Kotlin riutilizzabile avanzato:

È possibile impostare il flag direttamente utilizzando il metodo setter. In Kotlin orè la sostituzione per Java bit a bit o |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Se si prevede di utilizzarlo regolarmente, creare una funzione di estensione Intento

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

È quindi possibile chiamare direttamente questa funzione prima di iniziare l'intento

intent.clearStack()

Se è necessaria l'opzione per aggiungere ulteriori flag in altre situazioni, aggiungere un parametro opzionale alla funzione di estensione.

fun Intent.clearStack(additionalFlags: Int = 0) {
    flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

2
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

2

Prova sotto il codice,

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);

se sto usando come questa attività viene aggiornato ancora una volta chiama api ma tutte le statck esistenti in precedenza vengono cancellate
Harsha,

2

Per me nessuno dei metodi sopra elencati non funziona.

Basta fare questo per cancellare tutte le attività precedenti :

finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)

-1

A volte il tuo emulatore Android potrebbe non riuscire a connettere lo strumento DDMS eclipse e chiedere ad adb di avviarsi manualmente. In tal caso è possibile avviare o arrestare l'adb utilizzando il prompt dei comandi.


1
A volte il tuo emulatore Android potrebbe non riuscire a connettere lo strumento DDMS eclipse e chiedere ad adb di avviarsi manualmente. In tal caso è possibile avviare o arrestare l'adb utilizzando il prompt dei comandi. Intento i = new Intent (OldActivity.this, NewActivity.class); // imposta la nuova attività e cancella i flag i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
RajeshkumarG

-2

Ho trovato un hack troppo semplice, basta aggiungere questo nuovo elemento AndroidManifestcome: -

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

la android:noHistorysi svuota l'attività indesiderate da Pila.


2
Questo approccio può causare problemi su Android 6.0+, se si richiedono autorizzazioni in questa attività.
Vitaliy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.