Mal comportamiento de Backstack de la actividad cuando la actividad destruida

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

Para simplificar, hay un button en la activity B llamado "Destroy Activity A". Cuando un usuario hace clic en el button , la activity A debe ser destruida.

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

1) Supongamos que estoy en la activity B e presiono la tecla Inicio para mover la aplicación al fondo, entonces si uso otras aplicaciones de recursos pesados, el sistema Android matará a mi aplicación para liberar memoria. Entonces, si abro mi aplicación de tareas recientes, se reanudará la activity B , y se onCreate() método 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() , onResume() etc de la activity A no serán llamados hasta y a menos que vaya a la activity A presionando el back button . Así, el broadcast receiver no está registrado para escuchar el evento.

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

He estado buscando para resolver este problema durante mucho tiempo, pero no soy capaz de encontrar una respuesta adecuada. ¿Cuál es la mejor manera de manejar este escenario? ¿Se trata de un error de Android? Debe haber alguna solución para este problema.

Por favor, ayúdame.

Esto no se puede arreglar mientras se mantiene la lógica de difusión actual.

Matar a las actividades desde el backstack, imo, no es un enfoque correcto. Debe considerar seriamente 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 cuestión, el enfoque de AJ funciona, pero usted mencionó que tiene muchas actividades que necesitan ser asesinadas, su solución se vuelve muy complicada de implementar.

Lo que sugiero es el siguiente. Esto no podría ser la mejor idea, pero no puedo pensar en otra. Así que 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 aplicación. (Si no extendió la Application puede tenerla como variable estática

Primero tenemos que asegurarnos de que las activitiesToKill no se pierden cuando el sistema operativo mata la aplicación en poca memoria. En la 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 deben ser eliminadas en la lista, usando su nombre.

La lógica es la siguiente:

Digamos que usted tiene Actividades A, B, C, D y E

Desde la actividad E, presiona el botón y quieres matar 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, tenemos que comprobar 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 cancela a través de la transmisión.

Así que básicamente esto es lo que sucede en cada escenario.

Si la aplicación se ejecuta normalmente y hace clic en el botón, se emite la transmisión y B y D se matan. Asegúrese de quitar B y D de las activitiesToKill

Si la aplicación se ha eliminado y restaurado, presiona el botón, la transmisión no tendrá ningún efecto, pero ha agregado B y D al objeto activitiesToKill . Por lo tanto, cuando hace clic de nuevo, la actividad se crea y el savedInstanceState no es nulo, la actividad está terminada.

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

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

En lugar de utilizar un ArrayList, utilice un HashMap<String, bool>

Cuando se crea la Actividad B, se registrará en el hashmap:

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

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

Luego en la creación on de la actividad de base debes comprobar si esta actividad está registrada en las actividadesToKill (el hashmap contiene la clave) Y el booleano es true lo matas (no te olvides devolverlo a false o quitar el llave)

Esto asegura que cada actividad se registre en el HashMap y la Actividad E no tenga conocimiento de todas las actividades a matar. Y no se olvide de quitarlos en caso de que la transmisión los mata.

Este enfoque también asegura que la actividad no se mata cuando se abre normalmente de una intención porque en ese caso onSaveInstanceState sería nulo en el onCreate, por lo que no sucederá nada.

Pueden realizarse comprobaciones más avanzadas en caso de que haya grupos de actividades que necesiten terminarse en diferentes condiciones (no sólo un clic de botón) para que pueda tener un HashMap de un HashMap para dividirlas en categorías.

También tenga en cuenta que puede utilizar getName en lugar de getSimpleName si tiene varias actividades con el mismo nombre pero diferentes paquetes.

Espero que mi explicación sea lo suficientemente clara como lo escribí desde mi cabeza, hágamelo saber si alguna área no está clara.

La mejor de las suertes

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

Alguna gente ha sugerido seguir esa Activity A listing el acontecimiento en método de onDestroy PERO si su Activity mató por OS sistema entonces nota aquí que no llamará esos método.

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

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

Si utiliza un Otto, puede intentarlo con un evento pegajoso. De lo contrario, necesitará un singleton para manejar el indicador o guardarlo en las preferencias compartidas.

Tendrás que comprobar esa bandera en tu método onCreate() antes de llamar a setContentView() , si el flag es true acaba de terminar la actividad.

Con la información que ha dado, ¿qué hay acerca de registrar la emisión en onCreate de la Actividad B después de comprobar si ya está registrado o no. Si onDestroy de la Actividad A se ha llamado en cualquiera de los escenarios que ha mencionado, entonces el registro de la difusión habría sido llamado. Por lo tanto, en ese caso, puede registrar su difusión en onCreate de la actividad B, para que pueda escucharla, incluso si sólo tiene la actividad B en su backstack.

¿Ha pensado en usar la emisión 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 a las actividades desde el backstack no es un enfoque correcto. Debe considerar seriamente cambiar la lógica de su navegación.

Una de las reglas principales con Activities es que no puedes confiar en que ninguna actividad esté viva excepto la actividad de primer plano . Lo que estás tratando de hacer con las transmisiones no tiene nada que ver con la pila trasera – la pila de atrás no garantiza que todas las actividades estén vivas en todo momento, pero se asegurará de que se vuelvan a crear cuando llegue el momento de pasar a primer plano.

En tu ejemplo (si entiendo lo que quieres hacer) necesitas navegar por algo debajo de A , por ejemplo, Actividad Z , y la pila se ve así: ZA-[B] . Hay un curso normal de los acontecimientos donde usted golpea back y le lleva a A , después después de otro golpe – a Z pero en un cierto caso (por ejemplo, presionando un botón) usted quiere volver a Z pasar por A – esto es un clásico Case para usar FLAG_ACTIVITY_CLEAR_TOP y ejecutar Z explícitamente :

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

Esto terminará ambos B y A , y entregará la intención a Z Probablemente también necesitará el flag FLAG_ACTIVITY_SINGLE_TOP , preste mucha atención a la descripción de FLAG_ACTIVITY_CLEAR_TOP , hay algunos trucos que debe considerar.

Muchas soluciones me vinieron a la mente, pero como usted no ha proporcionado mucha información acerca de su aplicación, creo que esto debería funcionar en general.

En lugar de disparar una emisión para matar la actividad A, sólo ejecute el código siguiente cuando se presiona el botón "Activar 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 código siguiente 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 "singleTop" modo de inicio 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á, lo que eliminará de la pila.
  • Si la Actividad A ha sido destruida pero aún está presente en la pila de actividades (que se iniciará al pulsar el botón Atrás), se iniciará, se llevará a la parte delantera y se finalizará, eliminándola de la pila de actividades.
  • Si la actividad A ya ha sido destruida y no está presente en la pila de actividades, y todavía presiona el botón "Quitar actividad A", se iniciará, se llevará a la parte delantera y se terminará.

Generalmente, usted no debe ver ninguna parpadeo.

Basándose en esta idea, puede crear una solución de mejor rendimiento para su aplicación en particular. Por ejemplo, puede utilizar FLAG_ACTIVITY_CLEAR_TOP y finalizar la Actividad A en onBackPressed () de la Actividad B.

1) Puedes contener información sobre la actividad que quieres destruir en alguna clase estática. Si puede haber pocas actividades, puede mantener la matriz de valores int , por ejemplo:

 static final int ACTIVITY_DESTROY_FLAG_ACTIVITY_A = 1 static final int ACTIVITY_DESTROY_FLAG_ACTIVITY_B = 2 

En onResume of activity verifique si debe ser destruido. Para Activity_A podría parecer:

 if (StaticClass.activityArray.contains(ACTIVITY_DESTROY_FLAG_ACTIVITY_A)) { finish(); } 

2) Con anticipación a su mecanismo de recibo de difusión, puede poner actividad en Intent por intent.putExtra("activity", Activity_A.this) y enviarlo con difusión de onCreate de Activity_A . En Activity_B registre el receptor de broadcast y en los botones onClick call ((Activity) intent.getExtras().get("activity")).finish() . Bueno, sólo un receptor más – ¿va a gastar recursos para ello?

  • Administrador de alarmas programado no funciona Android
  • Determine el clic de addAction para notificaciones de Android
  • ¿Por qué no hay instrumentación de prueba para BroadcastReceiver?
  • Android: respuesta de la actividad del mercado
  • Comprobar resultCode en BroadcastReceiver de Android?
  • Android volátil no funciona?
  • BOOT_COMPLETED no funciona en Android
  • La notificación no se elimina al hacer clic en el botón de acción incluso después de proporcionar el ID de notificación
  • Ampliar broadcastreceptor y seguimiento de Google Analytics INSTALL_REFERRER
  • Mover un mensaje a la bandeja de entrada desde la base de datos
  • PACKAGE_ADDED BroadcastReceiver no funciona
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.