remove popbackstack from backstack activity android android-activity broadcastreceiver android-lifecycle back-stack

popbackstack - fragment back navigation android



Mala conducta de Backstack de actividad cuando la actividad destruida (7)

Si su Activity A ha sido destruida por el sistema operativo Android, entonces no hay forma de seguirla.

Algunas personas han sugerido rastrear esa Activity A al enumerar el evento en el método onDestroy , PERO si tu Activity onDestroy por el Sistema Operativo, entonces observa aquí que no llamará a esos métodos.

Tengo dos actividades; digamos A y B. En la activity A hay un receptor de difusión registrado que escucha un evento en particular que finalizará la actividad A. Estoy registrando el receptor de difusión en onCreate() y destruyéndolo en onDestroy() de la activity A

Para simplificar, hay un button en la activity B llamado "Destruir la actividad A". Cuando un usuario hace clic en el button , la activity A debe destruirse.

Normalmente todo esto funciona sin problemas, pero el problema ocurre en los siguientes escenarios:

1) Supongamos que estoy en la activity B y presiono la tecla de inicio para mover la aplicación al fondo, y si uso otras aplicaciones con muchos recursos, el sistema Android matará mi aplicación para liberar memoria. Luego, si abro mi aplicación desde tareas recientes, la activity B se reanudará, y se onCreate() el onCreate() , onResume() etc. Ahora presiono el button para destruir la activity A , pero la actividad A ya ha sido destruida, por lo que los onCreate() activity A onCreate() , onResume() etc. no se onResume() hasta que vaya a la activity A pulsando el back button . Por lo tanto, el broadcast receiver no está registrado para escuchar el evento.

2) El mismo problema surgirá cuando el usuario haya seleccionado "No guardar actividades" desde las opciones del Desarrollador en la configuración del dispositivo.

He estado buscando resolver este problema durante mucho tiempo, pero no puedo encontrar una respuesta adecuada. ¿Cuál es la mejor manera de manejar este escenario? ¿Es esto un error de Android? Debería haber alguna solución para este problema.

Por favor, ayúdame.


¿Has considerado usar Sticky Broadcast ? También puede registrar su receptor en el nivel de aplicación (en manifiesto) y escuchar este evento independientemente del estado de la Activity A

Pero, como ya dijo Youssef , matar actividades desde el backstack no es un enfoque correcto. Debería considerar cambiar la lógica de su navegación.


Con la información que ha proporcionado, ¿qué le parece registrar la transmisión en Crear la actividad B después de verificar si ya está registrada o no? Si onDestroy of Activity A ha sido llamado en cualquiera de los escenarios que ha mencionado, entonces se habría llamado al anulación de registro de Broadcast. Entonces, en ese caso, puede registrar su difusión en onCreate of Activity B, para que pueda escucharla, incluso si solo tiene la Activity B en su backstack.


Esto no se puede arreglar manteniendo su lógica de transmisión actual.

Las actividades de asesinato de la backstack, imo, no son un enfoque correcto. Debería considerar cambiar la lógica de su navegación.

Pero si su proyecto es grande y el tiempo es un problema, y ​​la refactorización está fuera de discusión, el enfoque de AJ funciona, pero mencionó que tiene muchas actividades que deben ser eliminadas, su solución se vuelve muy difícil de implementar.

Lo que sugiero es lo siguiente. Esta podría no ser la mejor idea, pero no puedo pensar en otra. Entonces tal vez eso podría ayudar.

Usted debe tener lo siguiente:

  • Una actividad base para todas sus actividades.
  • Un objeto ArrayList<String> activitiesToKill en el nivel de la aplicación. (Si no amplió la Application , puede tenerla como variable estática

Primero debemos asegurarnos de que las activitiesToKill ToKill no se pierdan cuando el SO mata la aplicación en poca memoria. En BaseActivity guardamos la lista durante onSaveInstanceState y la restauramos en onRestoreInstanceState

@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable("activitiesToKill", activitiesToKill); } private void onRestoreInstanceState(Bundle state) { if (state != null) { activitiesToKill = (ArrayList<String>) state.getSerializable("activitiesToKill"); super.onRestoreInstanceState(state); }

}

La idea aquí es guardar qué actividades deberían eliminarse en la lista, usando su nombre.

La lógica es la siguiente:

Digamos que tienes Actividades A, B, C, D y E

Desde la Actividad E, presiona el botón y quiere matar a B y D

Cuando presiona el botón en E, agrega los nombres de B y D al objeto activitiesToKill .

activitiesToKill.add(B.class.getSimpleName() activitiesToKill.add(D.class.getSimpleName()

En el método onCreate de BaseActivity, debemos verificar si el

if(savedInstanceState != null) { //The activity is being restored. We check if the it is in the lest to Kill and we finish it if(activitiesToKill.contains(this.getClass().getSimpleName())) { activitiesToKill.remove(this.getClass().getSimpleName()) finish(); } }

Asegúrese de eliminar el nombre de la actividad si se elimina a través de la transmisión.

Entonces, básicamente, esto es lo que sucede en cada escenario.

Si la aplicación se ejecuta normalmente, y haces clic en el botón, la transmisión se envía y B y D serán asesinados. Asegúrese de eliminar B y D de las activitiesToKill

Si la aplicación fue eliminada y restaurada, presiona el botón, la transmisión no tendrá efecto, pero habrá agregado B y D al objeto activitiesToKill . Por lo tanto, cuando hace clic atrás, se crea la actividad y savedInstanceState no es nulo, la actividad finaliza.

Este enfoque considera que la actividad E sabe qué actividades tiene que matar.

En caso de que NO sepa qué actividades matar desde E, tiene que modificar esta lógica ligeramente:

En lugar de usar una ArrayList usa un HashMap<String, bool>

Cuando se crea la Actividad B, se registrará a sí mismo en el hashmap:

activitiesToKill.put(this.class.getSimpleName(), false)

Luego, de la Actividad E, todo lo que tiene que hacer es establecer todas las entradas en true

Luego, en la creación de la actividad base, debe verificar si esta actividad está registrada en las actividadesToKill (el hashmap contiene la clave) Y si el booleano es true lo mata (no olvide devolverlo a falso, o elimine el llave)

Esto asegura que cada actividad se registre en HashMap y la Actividad E no tenga las mejores actividades para matar. Y no olvides eliminarlos en caso de que la transmisión los mate.

Este enfoque también asegura que la actividad no muera cuando se abre normalmente desde un intento porque en ese caso onSaveInstanceState sería nulo en onCreate, por lo que no sucederá nada.

Se pueden realizar comprobaciones más avanzadas en caso de que tenga grupos de actividades que deban terminarse a través de diferentes condiciones (no solo haga clic en un botón) para que pueda tener un HashMap de un HashMap para dividirlos en categorías.

También tenga en cuenta que puede usar getName en lugar de getSimpleName si tiene múltiples actividades con el mismo nombre pero diferentes paquetes.

Espero que mi explicación sea lo suficientemente clara como la escribí de mi cabeza, avíseme si alguna área no está clara.

La mejor de las suertes


No sé si es posible manejar esto de una manera "adecuada".

Lo que viene a mi mente es marcar la actividad A de alguna manera. No puede usar startActivityForResult() porque recibirá el resultado antes de que se onResume() es decir, la IU ya estaba inflada.

Si usa un Otto, podría intentarlo con un evento adhesivo. De lo contrario, necesitará un singleton para manejar la bandera o guardarla en las preferencias compartidas.

Deberá verificar ese indicador en su método onCreate() antes de llamar a setContentView() ; si el indicador es verdadero, simplemente termine la actividad.


Se me ocurrieron muchas soluciones, pero como no ha proporcionado mucha información sobre su aplicación, creo que esto debería funcionar en general.

En lugar de disparar una transmisión para matar a la Actividad A, solo ejecuta el siguiente código cuando se presiona el botón "Matar Actividad A" en la Actividad B.

Intent intent = new Intent(getApplicationContext(), ActivityA.class); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); intent.putExtra("EXIT", true); startActivity(intent);

Agregue el siguiente código en la actividad A

@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (intent.getBooleanExtra("EXIT", false)) { finish(); } } protected void onCreate(Bundle savedInstanceState) { //Ideally, there should not be anything before this super.onCreate(savedInstanceState); if(getIntent().getBooleanExtra("EXIT", false)){ finish(); return; }

En el conjunto de manifiesto, el modo de inicio "singleTop" para la actividad A.

<activity android:name=".ActivityA" ... android:launchMode="singleTop" />

Esto tendrá las siguientes consecuencias:

  • Si la Actividad A ya se está ejecutando, se llevará al frente de la pila de actividades y se terminará, por lo que se eliminará de la pila.
  • Si la Actividad A ha sido destruida pero aún está presente en la pila de actividades (que se lanzará cuando se presiona el botón Atrás), se iniciará, se colocará al frente y se terminará, y así se eliminará de la pila de actividades.
  • Si la actividad A ya se ha destruido y no está presente en la pila de actividades, y aún se presiona el botón "Eliminar actividad A", se iniciará, se colocará al frente y finalizará.

En general, no deberías ver ningún parpadeo.

Basado en esta idea, puede construir una solución de mejor rendimiento para su aplicación en particular. Por ejemplo, puede usar FLAG_ACTIVITY_CLEAR_TOP y finalizar la Actividad A en onBackPressed () de Activity B.


Una de las reglas principales de Activities es que no puede confiar en que ninguna actividad esté viva, excepto la actividad en primer plano . Lo que estás tratando de hacer con las transmisiones no tiene nada que ver con la pila de respaldo: la pila de respaldo no garantiza que todas las actividades estén activas en todo momento, pero se asegurará de que se vuelvan a crear cuando sea el momento de ir a primer plano.

En tu ejemplo (si entiendo lo que pretendes hacer) necesitas navegar a algo debajo de A , digamos, Actividad Z , y la pila se ve así: ZA-[B] . Hay un curso normal de eventos en el que respondes y te lleva a A , luego, después de otro golpe, a Z pero en cierto caso (por ejemplo, presionando un botón) quieres volver a Z pasando por alto A: este es un clásico caso de usar FLAG_ACTIVITY_CLEAR_TOP e iniciar Z explícitamente :

Intent intent = new Intent(this, ActivityZ.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent);

Esto terminará tanto B como A , y entregará el intento a Z Probablemente necesites también el indicador FLAG_ACTIVITY_SINGLE_TOP , presta mucha atención a la descripción de FLAG_ACTIVITY_CLEAR_TOP , hay algunos trucos que debes considerar.