with viewpager studio practice fragments example best android android-fragments android-viewpager

android - studio - IllegalStateException: no se puede realizar esta acción después de onSaveInstanceState con ViewPager



slide android studio (26)

Estoy obteniendo informes de usuarios de mi aplicación en el mercado, entregando la siguiente excepción:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109) at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399) at android.app.Activity.onBackPressed(Activity.java:2066) at android.app.Activity.onKeyUp(Activity.java:2044) at android.view.KeyEvent.dispatch(KeyEvent.java:2529) at android.app.Activity.dispatchKeyEvent(Activity.java:2274) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855) at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277) at android.app.Activity.dispatchKeyEvent(Activity.java:2269) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855) at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277) at android.app.Activity.dispatchKeyEvent(Activity.java:2269) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803) at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880) at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853) at android.view.ViewRoot.handleMessage(ViewRoot.java:2028) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:132) at android.app.ActivityThread.main(ActivityThread.java:4028) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:491) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) at dalvik.system.NativeStart.main(Native Method)

Aparentemente tiene algo que ver con un FragmentManager, que no uso. El stacktrace no muestra ninguna de mis propias clases, por lo que no tengo idea de dónde ocurre esta excepción y cómo prevenirla.

Para el registro: tengo un tabhost, y en cada pestaña hay un ActivityGroup cambiando entre Actividades.


A partir de la versión 24.0.0 de la biblioteca de soporte, puede llamar al método FragmentTransaction.commitNow() que realiza esta transacción de forma síncrona en lugar de llamar a commit() seguido de executePendingTransactions() . Como dice la documentation este enfoque aún mejor:

Llamar a commitNow es preferible a llamar a commit () seguido de executePendingTransactions () ya que este último tendrá el efecto secundario de intentar comprometer todas las transacciones pendientes actualmente, ya sea el comportamiento deseado o no.


Añade esto a tu actividad

@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (outState.isEmpty()) { // Work-around for a pre-Android 4.2 bug outState.putBoolean("bug:fix", true); } }


Aquí hay una solución diferente a este problema.

Al usar una variable miembro privada, puede configurar los datos devueltos como un intento que luego puede procesarse después de super.onResume ();

Al igual que:

private Intent mOnActivityResultIntent = null; @Override protected void onResume() { super.onResume(); if(mOnActivityResultIntent != null){ ... do things ... mOnActivityResultIntent = null; } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data){ if(data != null){ mOnActivityResultIntent = data; } }


Compruebe si la actividad es isFinishing() antes de mostrar el fragmento y preste atención a commitAllowingStateLoss() .

Ejemplo:

if(!isFinishing()) { FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); DummyFragment dummyFragment = DummyFragment.newInstance(); ft.add(R.id.dummy_fragment_layout, dummyFragment); ft.commitAllowingStateLoss(); }


Cortesía: Solución para IllegalStateException

Este problema me había molestado durante mucho tiempo, pero afortunadamente llegué con una solución concreta para ello. Una explicación detallada de esto está aquí .

El uso de commitAllowStateloss () puede evitar esta excepción, pero podría generar irregularidades en la interfaz de usuario. Hasta ahora hemos entendido que se produce la excepción IllegalStateException cuando intentamos enviar un fragmento después de que se pierde el estado de la actividad, por lo que solo debemos retrasar la transacción hasta que se restaure el estado .Se puede hacer simplemente así

Declarar dos variables booleanas privadas.

public class MainActivity extends AppCompatActivity { //Boolean variable to mark if the transaction is safe private boolean isTransactionSafe; //Boolean variable to mark if there is any transaction pending private boolean isTransactionPending;

Ahora, en onPostResume () y onPause, configuramos y desarmamos nuestra variable booleana isTransactionSafe. La idea es marcar la respuesta segura a salvo cuando la actividad está en primer plano, por lo que no hay posibilidad de que Stateloss.

/* onPostResume is called only when the activity''s state is completely restored. In this we will set our boolean variable to true. Indicating that transaction is safe now */ public void onPostResume(){ super.onPostResume(); isTransactionSafe=true; } /* onPause is called just before the activity moves to background and also before onSaveInstanceState. In this we will mark the transaction as unsafe */ public void onPause(){ super.onPause(); isTransactionSafe=false; } private void commitFragment(){ if(isTransactionSafe) { MyFragment myFragment = new MyFragment(); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.frame, myFragment); fragmentTransaction.commit(); } }

-Lo que hemos hecho hasta ahora se guardará de IllegalStateException pero nuestras transacciones se perderán si se realizan después de que la actividad pase a un segundo plano, algo así como commitAllowStateloss (). Para ayudar con eso tenemos isTransactionPending variable booleana.

public void onPostResume(){ super.onPostResume(); isTransactionSafe=true; /* Here after the activity is restored we check if there is any transaction pending from the last restoration */ if (isTransactionPending) { commitFragment(); } } private void commitFragment(){ if(isTransactionSafe) { MyFragment myFragment = new MyFragment(); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.frame, myFragment); fragmentTransaction.commit(); isTransactionPending=false; }else { /* If any transaction is not done because the activity is in background. We set the isTransactionPending variable to true so that we can pick this up when we come back to foreground */ isTransactionPending=true; } }


Creo que estoy utilizando transaction.commitAllowingStateLoss(); No es la mejor solución. Esta excepción se producirá cuando se cambie la configuración de la actividad y se onSavedInstanceState() fragmento onSavedInstanceState() y, posteriormente, el método de devolución de llamada asíncrona intente enviar el fragmento.

Una solución simple podría ser verificar si la actividad está cambiando la configuración o no

por ejemplo, check isChangingConfigurations()

es decir

if(!isChangingConfigurations()) { //commit transaction. }

Echa un vistazo a androiddesignpatterns.com/2013/08/… enlace también


En lo que respecta a la gran respuesta de @Anthonyeef, aquí hay un código de ejemplo en Java:

private boolean shouldShowFragmentInOnResume; private void someMethodThatShowsTheFragment() { if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) { showFragment(); } else { shouldShowFragmentInOnResume = true; } } private void showFragment() { //Your code here } @Override protected void onResume() { super.onResume(); if (shouldShowFragmentInOnResume) { shouldShowFragmentInOnResume = false; showFragment(); } }


Encontré una solución sucia para este tipo de problema. Si aún desea mantener sus ActivityGroups por cualquier motivo (tuve motivos de limitación de tiempo), simplemente implementa

public void onBackPressed() {}

en su Activity y hacer un código de back allí. incluso si no existe un método de este tipo en dispositivos más antiguos, este método es llamado por los más nuevos.


Es octubre de 2017, y Google hace una biblioteca de soporte de Android con lo nuevo que se llama componente Lifecycle. Proporciona una nueva idea para este problema ''No se puede realizar esta acción después de onSaveInstanceState''.

En breve:

  • Use el componente del ciclo de vida para determinar si es la hora correcta para que aparezca su fragmento.

Versión más larga con explicación:

  • ¿Por qué sale este problema?

    Es porque está intentando usar FragmentManager de su actividad (¿cuál va a contener su fragmento, supongo?) Para cometer una transacción para su fragmento. Por lo general, parecería que está intentando realizar alguna transacción para un fragmento que se aproxima, mientras que la actividad del host ya llama savedInstanceState método savedInstanceState (el usuario puede tocar el botón de inicio, por lo que la actividad llama a onStop() , en mi caso es la razón)

    Por lo general, este problema no debería ocurrir: siempre intentamos cargar fragmentos en la actividad desde el principio, ya que el método onCreate() es el lugar perfecto para esto. Pero a veces esto sucede , especialmente cuando no puede decidir qué fragmento cargará en esa actividad, o está intentando cargar un fragmento desde un bloque de AsyncTask (o cualquier cosa llevará un poco de tiempo). El tiempo, antes de que ocurra realmente la transacción del fragmento, pero después del método onCreate() la actividad, el usuario puede hacer cualquier cosa. Si el usuario presiona el botón de inicio, lo que desencadena el método onSavedInstanceState() la actividad, onSavedInstanceState() una onSavedInstanceState() can not perform this action .

    Si alguien quiere profundizar en este tema, les sugiero que echen un vistazo a esta publicación del blog. Se ve profundamente dentro de la capa de código fuente y explica mucho al respecto. Además, le da la razón por la que no debería usar el método commitAllowingStateLoss() para solucionar este problema (confíe en mí, no ofrece nada bueno para su código)

  • ¿Cómo arreglar esto?

    • ¿Debo usar el método commitAllowingStateLoss() para cargar el fragmento? No, no deberías ;

    • ¿Debo anular el método onSaveInstanceState , ignorar super método super dentro de él? No, no deberías ;

    • ¿Debo usar la actividad mágica “ isFinishing para verificar si la actividad del host está en el momento adecuado para la transacción de fragmentos? Sí, esta parece ser la forma correcta de hacerlo.

  • Eche un vistazo a lo que puede hacer el componente Lifecycle .

    Básicamente, Google realiza una implementación dentro de la clase AppCompatActivity (y varias otras clases base que debería usar en su proyecto), lo que hace que sea más fácil determinar el estado actual del ciclo de vida . Eche un vistazo a nuestro problema: ¿por qué sucedería este problema? Es porque hacemos algo en el momento equivocado. Así que tratamos de no hacerlo, y este problema desaparecerá.

    Codifico un poco para mi propio proyecto, esto es lo que hago usando LifeCycle . Codifico en Kotlin.

val hostActivity: AppCompatActivity? = null // the activity to host fragments. It''s value should be properly initialized. fun dispatchFragment(frag: Fragment) { hostActivity?.let { if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){ showFragment(frag) } } } private fun showFragment(frag: Fragment) { hostActivity?.let { Transaction.begin(it, R.id.frag_container) .show(frag) .commit() }

Como muestro arriba. Verificaré el estado del ciclo de vida de la actividad del host. Con el componente Lifecycle dentro de la biblioteca de soporte, esto podría ser más específico. El código lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED) significa que, si el estado actual es al menos onResume , no más tarde que? Lo que hace que mi método no se ejecute durante otro estado de vida (como onStop ).

  • ¿Está todo hecho?

    Por supuesto no. El código que he mostrado le indica una nueva forma de evitar que la aplicación se bloquee. Pero si va al estado de onStop , esa línea de código no hará nada y, por lo tanto, no mostrará nada en su pantalla. Cuando los usuarios vuelvan a la aplicación, verán una pantalla vacía, esa es la actividad del host vacío que no muestra ningún fragmento. Es una mala experiencia (sí un poco mejor que un choque).

    Así que aquí me gustaría que pudiera haber algo mejor: la aplicación no se bloqueará si cobra vida después de onResume , el método de transacción es consciente del estado de la vida; además, la actividad intentará continuar con la acción de transacción de fragmento, después de que el usuario vuelva a nuestra aplicación.

    Agrego algo más a este método:

class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver { private val hostActivity: FragmentActivity? = _host private val lifeCycle: Lifecycle? = _host.lifecycle private val profilePendingList = mutableListOf<BaseFragment>() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun resume() { if (profilePendingList.isNotEmpty()) { showFragment(profilePendingList.last()) } } fun dispatcherFragment(frag: BaseFragment) { if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) { showFragment(frag) } else { profilePendingList.clear() profilePendingList.add(frag) } } private fun showFragment(frag: BaseFragment) { hostActivity?.let { Transaction.begin(it, R.id.frag_container) .show(frag) .commit() } } }

Mantengo una lista dentro de esta clase de dispatcher , para almacenar esos fragmentos que no tienen oportunidad de terminar la acción de transacción. Y cuando el usuario vuelva de la pantalla de inicio y @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) todavía hay un fragmento en espera de ser lanzado, irá al método resume() bajo la @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) . Ahora creo que debería estar funcionando como esperaba.


Hay muchos problemas relacionados con un mensaje de error similar. Compruebe la segunda línea de este seguimiento de pila en particular. Esta excepción está relacionada específicamente con la llamada a FragmentManagerImpl.popBackStackImmediate .

Esta llamada al método, como popBackStack , siempre fallará con la IllegalArgumentException si el estado de la sesión ya se ha guardado. Compruebe la fuente. No hay nada que puedas hacer para evitar que se lance esta excepción.

  • Eliminar la llamada a super.onSaveInstanceState no ayudará.
  • La creación del fragmento con commitAllowingStateLoss no ayudará.

Así es como observé el problema:

  • Hay un formulario con un botón de enviar.
  • Cuando se hace clic en el botón, se crea un diálogo y se inicia un proceso asíncrono.
  • El usuario hace clic en la tecla de inicio antes de que finalice el proceso; se llama a onSaveInstanceState .
  • El proceso se completa, se realiza una devolución de llamada y se intenta popBackStackImmediate .
  • Se lanza IllegalStateException .

Esto es lo que hice para resolverlo:

Como no es posible evitar la IllegalStateException en la devolución de llamada, IllegalStateException .

try { activity.getSupportFragmentManager().popBackStackImmediate(name); } catch (IllegalStateException ignored) { // There''s no way to avoid getting this if saveInstanceState has already been called. }

Esto es suficiente para evitar que la aplicación se bloquee. Pero ahora el usuario restaurará la aplicación y verá que el botón que pensaron que habían presionado no se presionó en absoluto (piensan). ¡El fragmento de la forma todavía se está mostrando!

Para solucionar esto, cuando se crea el diálogo, haga un estado para indicar que el proceso ha comenzado.

progressDialog.show(fragmentManager, TAG); submitPressed = true;

Y guarda este estado en el paquete.

@Override public void onSaveInstanceState(Bundle outState) { ... outState.putBoolean(SUBMIT_PRESSED, submitPressed); }

No te olvides de volver a onViewCreated en onViewCreated

Luego, al reanudar, deshacer los fragmentos si se intentó enviar previamente. Esto evita que el usuario vuelva a lo que parece ser un formulario sin enviar.

@Override public void onResume() { super.onResume(); if (submitPressed) { // no need to try-catch this, because we are not in a callback activity.getSupportFragmentManager().popBackStackImmediate(name); submitPressed = false; } }


La excepción se lanza aquí (en FragmentActivity):

@Override public void onBackPressed() { if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) { super.onBackPressed(); } }

En FragmentManager.popBackStatckImmediate() , FragmentManager.checkStateLoss() se llama en primer lugar. Esa es la causa de IllegalStateException . Vea la implementación a continuación:

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

Resuelvo este problema simplemente usando una bandera para marcar el estado actual de la actividad. Aquí está mi solución:

public class MainActivity extends AppCompatActivity { /** * A flag that marks whether current Activity has saved its instance state */ private boolean mHasSaveInstanceState; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onSaveInstanceState(Bundle outState) { mHasSaveInstanceState = true; super.onSaveInstanceState(outState); } @Override protected void onResume() { super.onResume(); mHasSaveInstanceState = false; } @Override public void onBackPressed() { if (!mHasSaveInstanceState) { // avoid FragmentManager.checkStateLoss()''s throwing IllegalStateException super.onBackPressed(); } } }


No use commitAllowingStateLoss (), solo debe usarse en los casos en que está bien que el estado de la interfaz de usuario cambie inesperadamente en el usuario.

https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss()

Si la transacción ocurre en ChildFragmentManager de parentFragment, use parentFragment.isResume () en el exterior para verificar.

if (parentFragment.isResume()) { DummyFragment dummyFragment = DummyFragment.newInstance(); transaction = childFragmentManager.BeginTransaction(); trans.Replace(Resource.Id.fragmentContainer, startFragment); }


Otra posible solución, que no estoy seguro de si ayuda en todos los casos (se origina here ):

@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { final View rootView = findViewById(android.R.id.content); if (rootView != null) { rootView.cancelPendingInputEvents(); } } }


Para evitar este problema, podemos usar el componente de arquitectura de navegación , que se introdujo en Google I / O 2018. El componente de arquitectura de navegación simplifica la implementación de la navegación en una aplicación de Android.


Por favor revise mi respuesta here . Básicamente solo tuve que:

@Override protected void onSaveInstanceState(Bundle outState) { //No call for super(). Bug on API Level > 11. }

No hagas la llamada a super() en el método saveInstanceState . Esto estaba arruinando las cosas ...

Este es un bug conocido en el paquete de soporte.

Si necesita guardar la instancia y agregar algo a su Bundle , puede usar lo siguiente:

@Override protected void onSaveInstanceState(Bundle outState) { outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE"); super.onSaveInstanceState(outState); }

Al final, la solución adecuada fue (como se ve en los comentarios) usar:

transaction.commitAllowingStateLoss();

al agregar o realizar la FragmentTransaction que estaba causando la Exception .


Posiblemente la solución más sencilla y sencilla que encontré en mi caso fue evitar que se desprenda el fragmento ofensivo de la pila en respuesta al resultado de la actividad. Así que cambiando esta llamada en mi onActivityResult() :

popMyFragmentAndMoveOn();

a esto:

new Handler(Looper.getMainLooper()).post(new Runnable() { public void run() { popMyFragmentAndMoveOn(); } }

Ayudó en mi caso.


Recibía esta excepción cuando presionaba el botón Atrás para cancelar el selector de intento en la actividad de mi fragmento de mapa. Resolví esto reemplazando el código de onResume (donde estaba inicializando el fragmento) a onstart () y la aplicación funciona bien. Espero que ayude.


Sé que hay una respuesta aceptada por @Ovidiu Latcu pero después de un tiempo, el error aún persiste.

@Override protected void onSaveInstanceState(Bundle outState) { //No call for super(). Bug on API Level > 11. }

Crashlytics sigue enviándome este extraño mensaje de error.

Sin embargo, el error ahora se produce solo en la versión 7+ (Nougat) Mi solución fue usar commitAllowingStateLoss () en lugar de commit () en el fragmento de transacción .

Esta post es útil para commitAllowingStateLoss () y nunca más tuvo un problema de fragmentos.

Para resumir, la respuesta aceptada aquí podría funcionar en versiones de Android anteriores a Nougat.

Esto podría ahorrarle a alguien unas horas de búsqueda. codificaciones felices <3 aplausos


Si está haciendo algo de FragmentTransaction en onActivityResult lo que puede hacer, puede establecer algún valor booleano dentro de onActivityResult, entonces en onResume puede hacer su FragmentTransaction sobre la base del valor booleano. Por favor, consulte el código de abajo.

@Override protected void onResume() { super.onResume; if(isSwitchFragment){ isSwitchFragment=false; bottomNavigationView.getTabAt(POS_FEED).select(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) { isSwitchFragment=true; } }


Siempre que intente cargar un fragmento en su actividad, asegúrese de que la actividad se reanude y no vaya a pausar el estado. En el estado de pausa, puede terminar perdiendo la operación de confirmación que se realizó.

Puede usar transaction.commitAllowingStateLoss () en lugar de transaction.commit () para cargar el fragmento

o

Crea un booleano y comprueba si la actividad no se va a activar.

@Override public void onResume() { super.onResume(); mIsResumed = true; } @Override public void onPause() { mIsResumed = false; super.onPause(); }

luego, mientras se carga la comprobación de fragmentos

if(mIsResumed){ //load the your fragment }


También he experimentado este problema y el problema ocurre cada vez que cambia el contexto de su FragmentActivity (por ejemplo, se modifica la orientación de la pantalla, etc.). Así que la mejor solución es actualizar el contexto desde su FragmentActivity .


Terminé creando un fragmento base y amplié todos los fragmentos de mi aplicación.

public class BaseFragment extends Fragment { private boolean mStateSaved; @CallSuper @Override public void onSaveInstanceState(Bundle outState) { mStateSaved = true; super.onSaveInstanceState(outState); } /** * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException * would otherwise occur. */ public void showAllowingStateLoss(FragmentManager manager, String tag) { // API 26 added this convenient method if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (manager.isStateSaved()) { return; } } if (mStateSaved) { return; } show(manager, tag); } }

Luego, cuando intento mostrar un fragmento, uso showAllowingStateLoss lugar de show

Me gusta esto:

MyFragment.newInstance() .showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

Llegué a esta solución de este PR: https://github.com/googlesamples/easypermissions/pull/170/files


Tuve exactamente el mismo problema. Ocurrió por la destrucción de la actividad anterior. Cuando respaldé la actividad anterior fue destruida. La actividad de base la pongo (INCORRECTA)

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SpinnerCustom2.setFragmentManager(getSupportFragmentManager()); onCreateDrawerActivity(savedInstanceState); }

Lo puse en onStart fue CORRECTO

@Override protected void onStart() { super.onStart(); SpinnerCustom2.setFragmentManager(getSupportFragmentManager()); }


Tuve un problema similar, el escenario fue así:

  • Mi actividad es agregar / reemplazar fragmentos de la lista.
  • Cada fragmento de lista tiene una referencia a la actividad, para notificar la actividad cuando se hace clic en un elemento de la lista (patrón de observador).
  • Cada fragmento de lista llama a setRetainInstance (true); en su método onCreate .

El método onCreate de la actividad fue así:

mMainFragment = (SelectionFragment) getSupportFragmentManager() .findFragmentByTag(MAIN_FRAGMENT_TAG); if (mMainFragment == null) { mMainFragment = new SelectionFragment(); mMainFragment.setListAdapter(new ArrayAdapter<String>(this, R.layout.item_main_menu, getResources().getStringArray( R.array.main_menu))); mMainFragment.setOnSelectionChangedListener(this); FragmentTransaction transaction = getSupportFragmentManager() .beginTransaction(); transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG); transaction.commit(); }

La excepción fue lanzada porque la configuración cambia cuando (dispositivo girado), la actividad se crea, el fragmento principal se recupera del historial del administrador de fragmentos y, al mismo tiempo, el fragmento ya tiene una referencia OLD a la actividad destruida

Cambiando la implementación a este resuelto el problema:

mMainFragment = (SelectionFragment) getSupportFragmentManager() .findFragmentByTag(MAIN_FRAGMENT_TAG); if (mMainFragment == null) { mMainFragment = new SelectionFragment(); mMainFragment.setListAdapter(new ArrayAdapter<String>(this, R.layout.item_main_menu, getResources().getStringArray( R.array.main_menu))); FragmentTransaction transaction = getSupportFragmentManager() .beginTransaction(); transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG); transaction.commit(); } mMainFragment.setOnSelectionChangedListener(this);

debe configurar sus escuchas cada vez que se crea la actividad para evitar la situación en la que los fragmentos tienen referencias a las instancias antiguas destruidas de la actividad.


CUIDADO , usar transaction.commitAllowingStateLoss() puede resultar en una mala experiencia para el usuario. Para obtener más información sobre por qué se produce esta excepción, consulte androiddesignpatterns.com/2013/08/… .


Solución corta y de trabajo:

Sigue los pasos simples

Pasos

Paso 1: onSaveInstanceState estado onSaveInstanceState en el fragmento respectivo. Y eliminar el método super de ella.

@Override public void onSaveInstanceState( Bundle outState ) { }

Paso 2: utilizar fragmentTransaction.commitAllowingStateLoss( );

en lugar de fragmentTransaction.commit( ); mientras se fragmentan las operaciones.