IllegalStateException: impossible d’effectuer cette action après onSaveInstanceState avec ViewPager

J’obtiens des rapports d’utilisateurs de mon application sur le marché, avec l’exception suivante:

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) 

Apparemment, cela a quelque chose à voir avec un FragmentManager, que je n’utilise pas. La stacktrace ne montre aucune de mes propres classes, donc je ne sais pas où cette exception se produit et comment la prévenir.

Pour mémoire: j’ai un tabhost, et dans chaque onglet il y a un basculement ActivityGroup entre les activités.

S’il vous plaît vérifier ma réponse ici . Fondamentalement, je devais juste:

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

Ne faites pas l’appel à super() sur la méthode saveInstanceState . Cela gâchait les choses …

Ceci est un bogue connu dans le package de support.

Si vous avez besoin de sauvegarder l’instance et d’append quelque chose à votre outState vous pouvez utiliser les éléments suivants:

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

En fin de compte, la bonne solution était (comme on le voit dans les commentaires) d’utiliser:

 transaction.commitAllowingStateLoss(); 

lors de l’ajout ou de l’exécution de la FragmentTransaction Transaction à l’origine de l’ Exception .

Il existe de nombreux problèmes liés à un message d’erreur similaire. Vérifiez la deuxième ligne de cette trace de stack particulière. Cette exception est spécifiquement liée à l’appel à FragmentManagerImpl.popBackStackImmediate .

Cet appel de méthode, comme popBackStack , échouera toujours avec IllegalArgumentException si l’état de la session a déjà été enregistré. Vérifiez la source. Vous ne pouvez rien faire pour empêcher que cette exception soit lancée.

  • Supprimer l’appel à super.onSaveInstanceState pas.
  • Créer le fragment avec commitAllowingStateLoss pas.

Voici comment j’ai observé le problème:

  • Il y a un formulaire avec un bouton de soumission.
  • Lorsque vous cliquez sur le bouton, une boîte de dialog est créée et un processus asynchrone démarre.
  • L’utilisateur clique sur la clé d’accueil avant la fin du processus – onSaveInstanceState est appelé.
  • Le processus se termine, un rappel est effectué et popBackStackImmediate est tenté.
  • IllegalStateException est IllegalStateException .

Voici ce que j’ai fait pour le résoudre:

Comme il n’est pas possible d’éviter l’ IllegalStateException dans le rappel, attrapez et ignorez-le.

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

Ceci est suffisant pour empêcher l’application de tomber en panne. Mais maintenant, l’utilisateur restaure l’application et voit que le bouton qu’ils pensaient avoir pressé n’a pas été pressé du tout (ils pensent). Le fragment de formulaire est toujours visible!

Pour résoudre ce problème, lorsque la boîte de dialog est créée, définissez un état indiquant que le processus a démarré.

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

Et enregistrez cet état dans le bundle.

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

N’oubliez pas de le recharger dans onViewCreated

Ensuite, lors de la reprise, annuler les fragments si submit a déjà été tenté. Cela empêche l’utilisateur de revenir à ce qui semble être un formulaire non soumis.

 @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; } } 

Vérifiez si l’activité isFinishing() avant d’afficher le fragment et faites attention à commitAllowingStateLoss() .

Exemple:

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

Voici une solution différente à ce problème.

En utilisant une variable membre privée, vous pouvez définir les données renvoyées sous la forme d’une intention qui peut ensuite être traitée après super.onResume ();

Ainsi:

 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; } } 

Nous sums en octobre 2017 et Google fait d’Android Support Library le nouvel élément du cycle de vie des appels. Il fournit une nouvelle idée pour ceci ‘Impossible d’effectuer cette action après le problème de onSaveInstanceState’.

En bref:

  • Utilisez le composant du cycle de vie pour déterminer si le moment est opportun pour afficher votre fragment.

Version plus longue avec expliquer:

  • pourquoi ce problème est sorti?

    C’est parce que vous essayez d’utiliser FragmentManager de votre activité (qui va contenir votre fragment, je suppose?) Pour valider une transaction pour vous fragmenter. Habituellement, il semblerait que vous essayiez d’effectuer des transactions pour un fragment à venir, alors que l’activité hôte appelle déjà la méthode savedInstanceState (l’utilisateur peut toucher le bouton d’accueil pour que l’activité appelle onStop() dans mon cas, c’est la raison))

    Habituellement, ce problème ne devrait pas se produire – nous essayons toujours de charger fragment en activité au tout début, comme la méthode onCreate() est un endroit parfait pour cela. Mais parfois cela se produit , surtout lorsque vous ne pouvez pas décider quel fragment vous allez charger dans cette activité, ou que vous essayez de charger un fragment à partir d’un bloc AsyncTask (ou que quelque chose prendra un peu de temps). Le temps, avant que la transaction de fragment ne se produise réellement, mais après la méthode onCreate() l’activité, l’utilisateur peut faire n’importe quoi. Si l’utilisateur appuie sur le bouton d’accueil, ce qui déclenche la méthode onSavedInstanceState() l’activité, il can not perform this action plantage d’ can not perform this action .

    Si quelqu’un veut approfondir ce numéro, je lui suggère de jeter un coup d’œil à cet article. Il regarde au plus profond de la couche de code source et en explique beaucoup. De plus, cela explique pourquoi vous ne devriez pas utiliser la méthode commitAllowingStateLoss() pour contourner ce commitAllowingStateLoss() (croyez-moi, cela n’offre rien de bon pour votre code)

  • Comment régler ceci?

    • Dois-je utiliser la méthode commitAllowingStateLoss() pour charger un fragment? Non, vous ne devriez pas

    • Dois-je remplacer la méthode onSaveInstanceState , ignorer la méthode super intérieur? Non, vous ne devriez pas

    • Dois-je utiliser l’activité magique isFinishing intérieur pour vérifier si l’activité de l’hôte est au bon moment pour la transaction de fragment? Oui, cela ressemble à la bonne façon de faire.

  • Jetez un coup d’œil à ce que le composant Lifecycle peut faire.

    Fondamentalement, Google effectue une mise en œuvre au sein de la classe AppCompatActivity (et de plusieurs autres classes de base à utiliser dans votre projet), ce qui facilite la détermination de l’état du cycle de vie actuel . Retournez à notre problème: pourquoi ce problème se produirait-il? C’est parce que nous faisons quelque chose au mauvais moment. Nous essayons donc de ne pas le faire et ce problème disparaîtra.

    Je code un peu pour mon propre projet, voici ce que je fais avec LifeCycle . Je code à 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() } 

Comme je le montre ci-dessus. Je vérifierai l’état du cycle de vie de l’activité de l’hôte. Avec le composant Lifecycle dans la bibliothèque de support, cela pourrait être plus spécifique. Le code lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED) signifie, si l’état actuel est au moins onResume , pas plus tard que lui? Ce qui garantit que ma méthode ne sera pas onStop pendant un autre état de vie (comme onStop ).

  • Est-ce que tout est fait?

    Bien sûr que non. Le code que j’ai montré indique un nouveau moyen d’empêcher l’application de tomber en panne. Mais si cela se passe à l’état de onStop , cette ligne de code ne fera rien et ne montrera donc rien sur votre écran. Lorsque les utilisateurs reviennent à l’application, ils verront un écran vide, c’est-à-dire l’activité hôte vide ne montrant aucun fragment. C’est une mauvaise expérience (oui un peu mieux qu’un crash).

    Donc ici, je souhaite qu’il pourrait y avoir quelque chose de plus gentil: l’application ne plantera pas si elle arrive à l’état de vie plus tard que onResume , la méthode de transaction est consciente de l’état de vie; en outre, l’activité tentera de continuer à effectuer cette opération de transaction fragmentée, après le retour de l’utilisateur dans notre application.

    J’ajoute quelque chose de plus à cette méthode:

 class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver { private val hostActivity: FragmentActivity? = _host private val lifeCycle: Lifecycle? = _host.lifecycle private val profilePendingList = mutableListOf() @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() } } } 

Je maintiens une liste dans cette classe de dispatcher , pour stocker ces fragments, vous n’avez pas la chance de terminer l’action de transaction. Et lorsque l’utilisateur revient de l’écran d’accueil et constate qu’il y a encore des fragments en attente de lancement, il ira à la méthode resume() sous l’ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) . Maintenant, je pense que cela devrait fonctionner comme prévu.

Solution courte et fonctionnelle:

Suivez les étapes simples

Pas

Étape 1: Remplacez l’état onSaveInstanceState dans le fragment respectif. Et enlevez-en la méthode super.

  @Override public void onSaveInstanceState( Bundle outState ) { } 

Étape 2: Utilisez fragmentTransaction.commitAllowingStateLoss( );

au lieu de fragmentTransaction.commit( ); tout en fragmentant les opérations.

ATTENTION , l’utilisation de transaction.commitAllowingStateLoss() peut entraîner une mauvaise expérience pour l’utilisateur. Pour plus d’informations sur les raisons pour lesquelles cette exception est levée, consultez cette publication .

J’ai trouvé une solution sale pour ce genre de problème. Si vous souhaitez toujours conserver vos ActivityGroups pour quelque raison que ce soit (pour des raisons de limitation de temps), vous devez simplement implémenter

 public void onBackPressed() {} 

dans votre Activity et faire back code back là-dedans. même s’il n’existe pas de méthode sur les anciens appareils, cette méthode est appelée par les nouveaux.

N’utilisez pas commitAllowingStateLoss (), il ne doit être utilisé que dans les cas où l’état de l’interface utilisateur peut changer de manière inattendue pour l’utilisateur.

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

Au lieu de cela, utilisez if (fragment.isResume ()) vérifier en dehors de l’opération que vous avez rencontré cette exception IllegalStateException “Impossible d’effectuer cette action après onSaveInstanceState”

J’ai eu un problème similaire, le scénario était comme suit:

  • Mon activité ajoute / remplace des fragments de liste.
  • Chaque fragment de liste a une référence à l’activité, pour notifier l’activité lorsqu’un élément de liste est cliqué (modèle d’observateur).
  • Chaque fragment de liste appelle setRetainInstance (true); dans sa méthode onCreate .

La méthode onCreate de l’ activité était comme suit :

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

L’exception a été levée car lorsque la configuration change (périphérique pivoté), l’activité est créée, le fragment principal est extrait de l’historique du gestionnaire de fragment et en même temps, le fragment a déjà une référence OLD à l’ activité détruite.

changer la mise en œuvre à cela a résolu le problème:

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

vous devez définir vos écouteurs chaque fois que l’activité est créée pour éviter la situation où les fragments ont des références à d’anciennes instances détruites de l’activité.

J’obtenais cette exception lorsque j’appuyais sur le bouton pour annuler le sélecteur d’intention sur mon activité de fragment de carte. J’ai résolu ce problème en remplaçant le code de onResume (où j’initialisais le fragment) par onstart () et l’application fonctionne correctement.

Je pense en utilisant transaction.commitAllowingStateLoss(); n’est pas la meilleure solution. Cette exception sera levée lorsque la configuration de l’activité a été modifiée et que fragment onSavedInstanceState() est appelé et que par la suite, votre méthode de rappel asynchrone tente de valider fragment.

Une solution simple pourrait être de vérifier si l’activité change ou non de configuration

p.ex. vérifier isChangingConfigurations()

c’est à dire

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

Checkout ce lien aussi

Peut-être la solution la plus simple et la plus simple que j’ai trouvée dans mon cas était d’éviter que le fragment incriminé ne sorte de la stack en réponse au résultat de l’activité. Donc, changer cet appel dans mon onActivityResult() :

 popMyFragmentAndMoveOn(); 

pour ça:

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

aidé dans mon cas.

Chaque fois que vous essayez de charger un fragment de votre activité, assurez-vous que l’activité est en cours de reprise et ne pas mettre en pause l’état.En cas de pause, vous risquez de perdre l’opération de validation effectuée.

Vous pouvez utiliser transaction.commitAllowingStateLoss () au lieu de transaction.commit () pour charger fragment

ou

Créez un booléen et vérifiez si l’activité ne va pas se produire

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

puis en chargeant la vérification de fragment

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

Si vous faites FragmentTransaction dans onActivityResult, vous pouvez définir une valeur booléenne dans onActivityResult, puis dans onResume, vous pouvez effectuer votre FragmentTransaction sur la base de la valeur booléenne. Veuillez vous référer au code ci-dessous.

 @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; } } 

En ce qui concerne @Anthonyeef bonne réponse, voici un exemple de code 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(); } } 

Ajoutez ceci dans votre activité

 @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); } } 

A partir de la version 24.0.0 de la bibliothèque de support, vous pouvez appeler la méthode FragmentTransaction.commitNow() qui valide cette transaction de manière synchrone au lieu d’appeler commit() suivi de executePendingTransactions() . Comme le dit la documentation , cette approche est encore meilleure:

L’appel de commitNow est préférable à l’appel de commit () suivi de executePendingTransactions (), car ce dernier aura pour effet secondaire de tenter de valider toutes les transactions en attente, que ce soit le comportement souhaité ou non.

J’ai également rencontré ce problème et le problème survient chaque fois que le contexte de votre FragmentActivity est modifié (par exemple, l’orientation de l’écran est modifiée, etc.). La meilleure solution consiste donc à mettre à jour le contexte à partir de votre FragmentActivity .

L’exception est jetée ici (In FragmentActivity):

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

Dans FragmentManager.popBackStatckImmediate() , FragmentManager.checkStateLoss() est appelé en premier. C’est la cause de IllegalStateException . Voir la mise en œuvre ci-dessous:

 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); } } 

Je résous ce problème simplement en utilisant un drapeau pour marquer le statut actuel de l’activité. Voici ma solution:

 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(); } } 

}

J’ai fini par créer un fragment de base et en faire tous les fragments dans mon application

 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, Ssortingng)} that no-ops when an IllegalStateException * would otherwise occur. */ public void showAllowingStateLoss(FragmentManager manager, Ssortingng 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); } } 

Ensuite, lorsque j’essaie de montrer un fragment, j’utilise showAllowingStateLoss au lieu de show

comme ça:

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

Je suis venu à cette solution de ce PR: https://github.com/googlesamples/easypermissions/pull/170/files

Une autre solution possible, dont je ne suis pas sûr si elle aide dans tous les cas (origine ici ):

 @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(); } } } 

Je sais qu’il y a une réponse acceptée par @Ovidiu Latcu mais après un certain temps, l’erreur persiste.

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

Crashlytics m’envoie toujours ce message d’erreur étrange.

Cependant, l’erreur se produisant maintenant uniquement sur la version 7+ (Nougat) Mon correctif consistait à utiliser commitAllowingStateLoss () au lieu de commit () au fragmentTransaction.

Cet article est utile pour commitAllowingStateLoss () et n’a plus jamais de problème de fragment.

Pour résumer, la réponse acceptée ici pourrait fonctionner sur les versions Android pré-Nougat.

Cela pourrait faire économiser quelques heures à quelqu’un. heureux codages. <3 acclamations

J’ai eu exactement le même problème. C’est arrivé à cause de la destruction de l’activité précédente. lorsque j’ai soutenu l’activité précédente, il a été détruit. Je mets l’activité de base (WRONG)

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

Je l’ai mis dans onStart c’était correct

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

Pour contourner ce problème, nous pouvons utiliser le composant Architecture de navigation , introduit dans Google I / O 2018. Le composant d’architecture de navigation simplifie la mise en œuvre de la navigation dans une application Android.

Courtoisie: Solution pour IllegalStateException

Ce problème m’avait beaucoup embêté mais heureusement, je suis arrivé avec une solution concrète. Une explication détaillée de cela est ici .

Utiliser commitAllowStateloss () pourrait empêcher cette exception, mais conduirait à des irrégularités de l’interface utilisateur. Jusqu’ici, nous avons compris que IllegalStateException est rencontré lorsque nous essayons de valider un fragment après la perte de l’état de l’activité. .Il peut être fait simplement comme ça

Déclarez deux variables booléennes privées

  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; 

Maintenant, dans onPostResume () et onPause, nous définissons et désactivons notre variable booléenne isTransactionSafe. L’idée est de marquer la sécurité des transactions uniquement lorsque l’activité est en avant-plan, de sorte qu’il n’y a aucune chance de dépassement.

 /* 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(); } } 

-Que ce que nous avons fait jusqu’ici sauvera de IllegalStateException mais nos transactions seront perdues si elles sont faites une fois que l’activité est passée en arrière-plan, un peu comme commitAllowStateloss (). Pour aider avec cela, nous avons la variable booléenne isTransactionPending

 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; } }