Borre todo el historial y comience una nueva actividad en Android

332

¿Es posible comenzar una actividad en la pila, limpiando todo el historial antes?

La situación

Tengo una pila de actividades que va A-> B-> C o B-> C (la pantalla A selecciona el token de los usuarios, pero muchos usuarios solo tienen un token).

En la pantalla C, el usuario puede realizar una acción que invalida la pantalla B, por lo que la aplicación desea llevarlos a la pantalla A, independientemente de si ya está en la pila. La pantalla A debería ser el único elemento en la pila en mi aplicación.

Notas

Hay muchas otras preguntas similares, pero no he encontrado nada que responda a esta pregunta exacta. Intenté llamar getParent().finish(), esto siempre resulta en una excepción de puntero nulo. FLAG_ACTIVITY_CLEAR_TOPsolo funciona si la actividad ya está en la pila.

Casebash
fuente

Respuestas:

658

En el nivel de API 11, se agregó un nuevo Indicador de intención solo para esto: Intent.FLAG_ACTIVITY_CLEAR_TASK

Solo para aclarar, use esto:

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

Desafortunadamente para API lvl <= 10, aún no he encontrado una solución limpia para esto. La solución "DontHackAndroidLikeThis" es de hecho pura piratería. No deberías hacer eso. :)

Editar: Según el comentario de @ Ben Pearson , para API <= 10 ahora se puede usar la clase IntentCompat para lo mismo. Se puede usar la IntentCompat.FLAG_ACTIVITY_CLEAR_TASKbandera para borrar la tarea. Por lo tanto, también puede admitir el nivel 11 de pre API.

Akos Cz
fuente
23
Solo para aclarar, use esto: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
user123321
2
sin el Intent.FLAG_ACTIVITY_NEW_TASK, la aplicación a veces solo se cierra en Android 4
max4ever
22
IntentCompat también tiene un indicador para borrar la tarea ahora, por lo que puede admitir el nivel 11 anterior a la API - developer.android.com/reference/android/support/v4/content/…
Ben Pearson
10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK se ignora en dispositivos con nivel de API <10. developer.android.com/reference/android/support/v4/content/…
David
77
La bandera de IntentCompat es solo para evitar un bloqueo, pero no hace nada como dice @David.
Sloy
49

Caso 1: solo dos actividades A y B:

Aquí el flujo de actividad es A-> B. Al hacer clic en el botón Atrás de B, necesitamos cerrar la aplicación y luego al iniciar la Actividad B desde A solo llame al final (), esto evitará que Android almacene la Actividad A en Backstack.eg para que la actividad A sea Alojamiento / pantalla de inicio de la aplicación.

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

Caso 2: más de dos actividades:

Si hay un flujo como A-> B-> C-> D-> B y al hacer clic en el botón Atrás en la Actividad B mientras viene de la Actividad D. En ese caso, deberíamos usar.

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

Aquí, la Actividad B se iniciará desde el backstack en lugar de una nueva instancia debido a Intent.FLAG_ACTIVITY_CLEAR_TOP e Intent.FLAG_ACTIVITY_NEW_TASK borra la pila y la convierte en la primera, por lo que cuando presionamos el botón Atrás, se cerrará toda la aplicación.

Monish George
fuente
2
Esto funcionó para mí. Puse en TODAS las actividades esas banderas. En esas actividades, los botones de retroceso funcionan perfectamente yendo a la actividad anterior, y en la Actividad principal con Intención intent = new Intent (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (intento); terminar(); Toda la aplicación está cerrada, todavía en la memoria pero no activa, y si reinicia la aplicación va a la pantalla de
inicio
Esta debería ser la mejor respuesta. Si alguien tiene el mismo escenario conmigo: A-> B-> C-> D-> E -> (B) De E-> B debería tener un resultado: A-> B
Shem Alexis Chavez
39

Con la versión más reciente de Android> = API 16 use finishAffinity()

El enfoque es adecuado para> = API 16.

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • Es lo mismo que comenzar una nueva Actividad y borrar toda la pila.
  • O Reinicie a MainActivity / FirstActivity.
karan
fuente
1
¡Esto funcionó, las banderas no funcionaban en 4.xx para mí y esto funcionó perfectamente! Gracias
Jonathan Aste
1
Esta parece ser la respuesta correcta si su objetivo es terminar todas las actividades a continuación e incluir la actividad actual y comenzar una nueva actividad en su propia tarea.
ToBe
24

También pasé unas horas en esto ... y estoy de acuerdo en que FLAG_ACTIVITY_CLEAR_TOP suena como lo que quieres: borra toda la pila, excepto la actividad que se inicia, por lo que el botón Atrás sale de la aplicación. Sin embargo, como mencionó Mike Repass, FLAG_ACTIVITY_CLEAR_TOP solo funciona cuando la actividad que está iniciando ya está en la pila; cuando la actividad no está allí, la bandera no hace nada.

¿Qué hacer? Coloque la actividad que se inicia en la pila con FLAG_ACTIVITY_NEW_TASK, lo que hace que esa actividad sea el inicio de una nueva tarea en la pila del historial. Luego agregue el indicador FLAG_ACTIVITY_CLEAR_TOP.

Ahora, cuando FLAG_ACTIVITY_CLEAR_TOP vaya a buscar la nueva actividad en la pila, estará allí y se detendrá antes de que se borre todo lo demás.

Aquí está mi función de cierre de sesión; El parámetro Ver es el botón al que se adjunta la función.

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();
}
usuario2895402
fuente
1
¿te refieres a CLEAR_TASK en lugar de CLEAR_TOP?
Andy
14

No deberías cambiar la pila. El botón de retroceso de Android debería funcionar como en un navegador web.

Se me ocurre una forma de hacerlo, pero es un gran truco.

  • Realice sus actividades singleTaskagregándolas al AndroidManifest ejemplo:

    <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"/>
  • Extienda lo Applicationque tendrá la lógica de dónde ir.

Ejemplo:

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 a B:

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

De B ​​a C:

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

C ª:

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

y maneje el botón Atrás pop()desde la pila.

Una vez más, no deberías hacer esto :)

Macarse
fuente
Al final decido dejar la pila intacta y decirle al usuario que su pantalla actual no es válida
Casebash el
1
Muy frustrante que Android ya no nos permita administrar la pila de actividades de esta manera. Estaría tentado a usar esta solución en mis futuras aplicaciones de Android.
Cephron
44
Solo para que quede claro por qué esto no debería usarse: es una buena manera de crear pérdidas de memoria. En algún momento, el sistema operativo puede decidir eliminar las actividades en segundo plano, pero dado que Applicationtoma sus instancias, el sistema operativo no podrá liberar la RAM que queda de las actividades destruidas.
Vit Khudenko
@Arhimed ¿Hay otros problemas? La pérdida de memoria se puede reparar manteniendo solo referencias débiles.
Navin
1
@Navin sí, las fugas se pueden evitar con referencias débiles, pero si después de GC no habrá una referencia de Actividad en vivo, entonces todo el enfoque es inútil. Una vez más, no hagas esto, este es un enfoque incorrecto para Android.
Vit Khudenko
12

Inmediatamente después de comenzar una nueva actividad, usando startActivity, asegúrese de llamar finish()para que la actividad actual no se apile detrás de la nueva.

Keith Maurino
fuente
+1 Buena solución para evitar que se coloque exactamente una actividad en una situación determinada en la pila de historial.
marsbear
27
no funciona si tienes más de una actividad en la pila, el final solo borrará la actividad anterior pero no las demás ...
Necronet
5

Prueba esto:

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
fuente
4

Kotlin reutilizable avanzado:

Puede establecer la bandera directamente utilizando el método setter. En Kotlin se orencuentra el reemplazo para Java o bit a bit |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Si planea usar esto regularmente, cree una función de extensión de intención

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

Luego puede llamar directamente a esta función antes de comenzar la intención

intent.clearStack()

Si necesita la opción de agregar indicadores adicionales en otras situaciones, agregue un parámetro opcional a la función de extensión.

fun Intent.clearStack(additionalFlags: Int = 0) {
    flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
Gibolt
fuente
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
fuente
2

Prueba el siguiente código,

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
fuente
si estoy usando como esta actividad se actualiza una vez más, llame a la API pero previamente se borra todo statck
Harsha
2

Para mí ninguno de los métodos anteriores no funciona.

Simplemente haga esto para borrar toda la actividad anterior :

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
fuente
-1

A veces, su emulador de Android puede fallar al conectar la herramienta eclipse DDMS y solicitar que adb se inicie manualmente. En ese caso, puede iniciar o detener el adb utilizando el símbolo del sistema.

RajeshkumarG
fuente
1
A veces, su emulador de Android puede fallar al conectar la herramienta eclipse DDMS y solicitar que adb se inicie manualmente. En ese caso, puede iniciar o detener el adb utilizando el símbolo del sistema. Intención i = nueva intención (OldActivity.this, NewActivity.class); // establece la nueva tarea y borra las banderas i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
RajeshkumarG
-2

Encontré un hack demasiado simple, solo haga esto agregar un nuevo elemento AndroidManifestcomo: -

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

El android:noHistoryborrará su actividad no deseada de la pila.

Tauseef
fuente
2
Este enfoque puede causar problemas en Android 6.0+, si solicita permisos en esta Actividad.
Vitaliy A