savedinstancestate practices example best activity android android-fragments fragmentmanager android-savedstate onsaveinstancestate

android - practices - Verificando si el estado está guardado antes de confirmar un FragmentTransaction



save activity state android (8)

A veces veo la siguiente stacktrace para una confirmación que puede ocurrir cuando el usuario no está mirando la actividad (después de que se ha guardado el estado):

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327) at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338) at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595) at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

Mirando la fuente de Android, esto tiene total sentido:

private void checkStateLoss() { if (mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } if (mNoTransactionsBecause != null) { throw new IllegalStateException( "Can not perform this action inside of " + mNoTransactionsBecause); } }

Ahora, me pregunto si hay alguna forma (además de almacenar una variable de clase en (Guardar / Restaurar) InstanceState) para verificar si un fragmento se va a comprometer en un estado no deseado, de esta manera puedo almacenar la transacción para más adelante y hacer el compromiso en el momento apropiado.


A partir de la versión 26.0.0 de la biblioteca de soporte Beta 1, hay una nueva API disponible en las clases FragmentManager y Fragment :

FragmentManager y Fragment tienen un método isStateSaved () para permitir consultar si una transacción se permitirá o no sin pérdida de estado. Esto es especialmente útil para verificar cuando se maneja un evento onClick () antes de ejecutar cualquier transacción.

De documentos de android.support.v4.app.FragmentManager#isStateSaved() :

Devuelve verdadero si el estado de FragmentManager ya ha sido guardado por su host. Cualquier operación que cambiaría el estado guardado no se debería realizar si este método devuelve verdadero . Por ejemplo, cualquier método popBackStack () , como popBackStackImmediate () o cualquier FragmentTransaction utilizando commit () en lugar de commitAllowingStateLoss () cambiará el estado y dará como resultado un error.

Esta API se enviará con android.app.FragmentManager de Framework a partir de Android O.



Como no adjuntó ningún código de ejemplo, todo lo que puedo adivinar es que está utilizando el método "incorrecto" al realizar la transacción.

entonces, en lugar de utilizar FragmentTransaction.commit() , debe usar FragmentTransaction.commitAllowingStateLoss() .

Además, hay informes y soluciones sobre este tema (o más bien cambios en el comportamiento de la API) en esta publicación de blog de Google .



Desafortunadamente, Android Fragment no proporciona una verificación API si está bien para confirmar la transacción.

Pero podemos agregar un campo booleano en la actividad adjunta que nos ayuda a verificar. Por favor ref el siguiente código.

public class GlobalBaseActivity extends FragmentActivity { private boolean mAllowCommit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAllowCommit = true; } @Override protected void onSaveInstanceState(Bundle outState) { mAllowCommit = false; super.onSaveInstanceState(outState); } @Override protected void onResumeFragments() { mAllowCommit = true; super.onResumeFragments(); } public boolean allowFragmentCommit() { return mAllowCommit; } public void callbackOnEvent() { if (allowFragmentCommit()){ getFragmentManager().beginTransaction().add(new YourFragment(), TAG).commit(); } } }

En cuanto a por qué elegir onResumeFragment() como permitir el indicador de transacción, ref este buen blog . Explica la famosa IllegalStateException en detalle.


RuntimeExceptions (y IllegalStateException es uno de ellos) con frecuencia significa que su código es incorrecto sobre cómo intentó lograr algo. Tratar de manejar esa excepción (por ejemplo, atrapándola) también es incorrecta. Su código nunca debería comportarse de la misma forma que Android lanzaría una excepción como esta sobre usted.

Si usa Controladores y publica o envía mensajes para ser manejados en el futuro, debe borrar la cola antes de salir del estado reanudado. Además, no puede simplemente iniciar AsyncTask desde Activity y commit transaction en onPostExecute porque el usuario podría volver desde su actividad. Debe cancelarlo y verificar si fue cancelado.

Hay muchos ejemplos como estos y todos son causados ​​por fugas de memoria "temporales", como me gusta llamarlas.

Básicamente, su código es malo y, sin proporcionarlo, es imposible decir cómo.


Ver que también tuve este problema, no pude encontrar una solución en ningún lado. Intenté trabajar. Funciona así. Realice la transacción de fragmento desde un controlador en su lugar. El controlador debe invocarse con un DELAY de 2000 ms. P.ej. al llamar a makeTransaction

void makeTransaction() { handler.sendEmptyMessageDelayed(0,2000); } void actuallyMakeTransaction() { // transaction code } Handler handler = new Handler() { void handleMessage(Message msg) { actuallyMakeTransaction(); } }

Me solucionó el problema


ft.commitAllowingStateLose() es su mejor apuesta en esta situación. Sin embargo, como se dijo, no hay garantía de que su estado persista.