Je reçois des rapports d'utilisateurs de mon application sur le marché, générant 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. Le stacktrace ne montre aucune de mes propres classes, donc je n'ai aucune idée où cette exception se produit et comment l'empêcher.
Pour mémoire: j'ai un tabhost, et dans chaque onglet il y a un ActivityGroup qui bascule entre les activités.
android
android-fragments
android-viewpager
illegalstateexception
fragmenttransaction
nhaarman
la source
la source
FragmentManager
, Honeycomb l'est certainement. Cela se produit-il sur de vraies tablettes Honeycomb? Ou est-ce que quelqu'un exécute un Honeycomb piraté sur un téléphone ou quelque chose et c'est cette édition piratée qui a du mal?Réponses:
Veuillez vérifier ma réponse ici . En gros, je devais juste:
Ne faites pas l'appel à
super()
lasaveInstanceState
méthode. C'était gâcher les choses ...Il s'agit d'un bogue connu dans le package de support.
Si vous devez enregistrer l'instance et ajouter quelque chose à votre,
outState
Bundle
vous pouvez utiliser ce qui suit:En fin de compte, la bonne solution était (comme on le voit dans les commentaires) d'utiliser:
lors de l'ajout ou de l'exécution de la
FragmentTransaction
cause de laException
.la source
popBackStackImmediate
elle échoue immédiatement si l'état a été enregistré. Auparavant, l'ajout du fragment aveccommitAllowingStateLoss
ne joue aucun rôle. Mes tests montrent que cela est vrai. Il n'a aucun effet sur cette exception spécifique. Ce dont nous avons besoin, c'est d'unepopBackStackImmediateAllowingStateLoss
méthode.Il existe de nombreux problèmes associés avec un message d'erreur similaire. Vérifiez la deuxième ligne de cette trace de pile particulière. Cette exception est spécifiquement liée à l'appel à
FragmentManagerImpl.popBackStackImmediate
.Cet appel de méthode, comme
popBackStack
, échouera toujoursIllegalStateException
si l'état de session a déjà été enregistré. Vérifiez la source. Vous ne pouvez rien faire pour empêcher cette exception d'être levée.super.onSaveInstanceState
n'aidera pas.commitAllowingStateLoss
n'aidera pas.Voici comment j'ai observé le problème:
onSaveInstanceState
est appelé.popBackStackImmediate
est tenté.IllegalStateException
Est lancé.Voici ce que j'ai fait pour le résoudre:
Comme il n'est pas possible d'éviter le
IllegalStateException
dans le rappel, rattrapez-le et ignorez-le.Cela suffit pour empêcher l'application de planter. Mais maintenant, l'utilisateur va restaurer l'application et voir que le bouton sur lequel il pensait avoir appuyé n'a pas du tout été pressé (ils pensent). Le fragment de formulaire est toujours visible!
Pour résoudre ce problème, lorsque la boîte de dialogue est créée, créez un état pour indiquer que le processus a commencé.
Et enregistrez cet état dans le bundle.
N'oubliez pas de le recharger à nouveau dans
onViewCreated
Ensuite, lors de la reprise, annulez les fragments si la soumission a été tentée précédemment. Cela empêche l'utilisateur de revenir à ce qui semble être un formulaire non soumis.
la source
popBackStackImmediate
téléphone était appelé par Android lui-même?Vérifiez si l'activité
isFinishing()
avant de montrer le fragment et faites attention àcommitAllowingStateLoss()
.Exemple:
la source
DialogFragment
. Voir stackoverflow.com/questions/15729138/… pour d'autres bonnes solutions, stackoverflow.com/a/41813953/2914140 m'a aidé.Nous sommes en octobre 2017 et Google crée la bibliothèque de support Android avec les nouvelles choses appelées composant Lifecycle. Il fournit une nouvelle idée de ce problème «Impossible d'exécuter cette action après onSaveInstanceState».
En bref:
Version plus longue avec explication:
pourquoi ce problème est-il apparu?
C'est parce que vous essayez d'utiliser
FragmentManager
votre activité (qui va contenir votre fragment je suppose?) Pour valider une transaction pour votre fragment. Habituellement, cela donnerait l'impression que vous essayez de faire une transaction pour un fragment à venir, tandis que l'activité de l'hôte appelle déjà lasavedInstanceState
méthode (l'utilisateur peut arriver de toucher le bouton d'accueil, donc l'activité appelleonStop()
, dans mon cas, c'est la raison)Habituellement, ce problème ne devrait pas se produire - nous essayons toujours de charger le fragment dans l'activité au tout début, comme la
onCreate()
méthode est un endroit parfait pour cela. Mais parfois, cela se produit , surtout lorsque vous ne pouvez pas décider quel fragment vous allez charger pour cette activité, ou que vous essayez de charger un fragment à partir d'unAsyncTask
bloc (ou quoi que ce soit prendra un peu de temps). Le temps, avant que la transaction de fragment ne se produise réellement, mais après laonCreate()
méthode de l'activité , l'utilisateur peut tout faire. Si l'utilisateur appuie sur le bouton d'accueil, ce qui déclenche laonSavedInstanceState()
méthode de l'activité , il y aurait uncan not perform this action
crash.Si besoin de quelqu'un pour voir plus loin dans cette question, je leur suggère de jeter un oeil à ce blog post . Il semble profondément à l'intérieur de la couche de code source et explique beaucoup de choses à ce sujet. En outre, cela donne la raison pour laquelle vous ne devez pas utiliser la
commitAllowingStateLoss()
méthode pour contourner ce crash (croyez-moi, cela n'offre rien de bon pour votre code)Comment régler ceci?
Dois-je utiliser une
commitAllowingStateLoss()
méthode pour charger un fragment? Non, vous ne devriez pas ;Dois-je remplacer la
onSaveInstanceState
méthode, ignorer lasuper
méthode à l'intérieur? Non, vous ne devriez pas ;Dois-je utiliser l'
isFinishing
activité magique interne pour vérifier si l'activité hôte est au bon moment pour la transaction de fragment? Oui, cela ressemble à la bonne façon de faire.Jetez un œil à ce que le composant Lifecycle peut faire.
Fondamentalement, Google effectue une implémentation à l'intérieur de la
AppCompatActivity
classe (et plusieurs autres classes de base que vous devez utiliser dans votre projet), ce qui facilite la détermination de l'état actuel du cycle de vie . Revenons à 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 aura disparu.Je code un peu pour mon propre projet, voici ce que je fais en utilisant
LifeCycle
. Je code en Kotlin.Comme je le montre ci-dessus. Je vais vérifier l'état du cycle de vie de l'activité 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 moinsonResume
, au plus tard? Ce qui garantit que ma méthode ne sera pas exécutée pendant un autre état de vie (commeonStop
).Est-ce que tout est fait?
Bien sûr que non. Le code que j'ai montré indique une nouvelle façon d'empêcher l'application de planter. Mais si elle 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 l'activité de l'hôte vide ne montrant aucun fragment du tout. C'est une mauvaise expérience (ouais un peu mieux qu'un crash).Donc, ici, je souhaite qu'il puisse y avoir quelque chose de plus agréable: l'application ne se bloquera pas si elle arrive à l'état de vie plus tard
onResume
, la méthode de transaction est consciente de l'état de vie; en outre, l'activité tentera de continuer à terminer cette action de transaction de fragment, une fois que l'utilisateur sera revenu sur notre application.J'ajoute quelque chose de plus à cette méthode:
Je maintiens une liste à l'intérieur de cette
dispatcher
classe, pour stocker ces fragments, je n'ai pas la chance de terminer l'action de transaction. Et lorsque l'utilisateur revient de l'écran d'accueil et qu'il y a encore un fragment en attente d'être lancé, il ira à laresume()
méthode sous l'@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
annotation. Maintenant, je pense que cela devrait fonctionner comme je m'y attendais.la source
FragmentDispatcher
utilise-t-elle une liste pour stocker les fragments en attente s'il n'y a qu'un seul fragment restauré?Voici une solution différente à ce problème.
En utilisant une variable membre privée, vous pouvez définir les données renvoyées comme une intention qui peut ensuite être traitée après super.onResume ();
Ainsi:
la source
super.onActivityResult()
.Solution courte et fonctionnelle:
Suivez des étapes simples
Pas
Étape 1: remplacer l'
onSaveInstanceState
état dans le fragment respectif. Et supprimez la super méthode.Étape 2: utiliser
fragmentTransaction.commitAllowingStateLoss( );
au lieu de
fragmentTransaction.commit( );
tandis que les opérations de fragment.la source
ATTENTION , l'utilisation
transaction.commitAllowingStateLoss()
peut entraîner une mauvaise expérience pour l'utilisateur. Pour plus d'informations sur les raisons de la levée de cette exception, consultez cet article .la source
J'ai trouvé une sale solution pour ce genre de problème. Si vous voulez toujours garder votre
ActivityGroups
pour une raison quelconque (j'avais des raisons de limitation de temps), vous implémentez simplementdans votre
Activity
et faites un peu deback
code là-dedans. même s'il n'y a pas une telle méthode sur les appareils plus anciens, cette méthode est appelée par les plus récents.la source
N'utilisez pas commitAllowingStateLoss (), il ne doit être utilisé que dans les cas où il est acceptable que l'état de l'interface utilisateur change de façon inattendue sur l'utilisateur.
https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss ()
Si la transaction se produit dans ChildFragmentManager de parentFragment, utilisez parentFragment.isResume () extérieur pour vérifier à la place.
la source
J'ai eu un problème similaire, le scénario était le suivant:
La méthode onCreate de l' activité était la suivante:
L'exception a été levée parce que lorsque la configuration change (appareil en rotation), l'activité est créée, le fragment principal est récupéré de l'historique du gestionnaire de fragments et en même temps le fragment a déjà une VIEILLE référence à la activité détruite
changer l'implémentation en ceci a résolu le problème:
vous devez définir vos écouteurs chaque fois que l'activité est créée pour éviter que les fragments aient des références à d'anciennes instances détruites de l'activité.
la source
Si vous héritez de
FragmentActivity
, vous devez appeler la superclasse dansonActivityResult()
:Si vous ne faites pas cela et essayez d'afficher une boîte de dialogue de fragment dans cette méthode, vous pouvez obtenir des OP
IllegalStateException
. (Pour être honnête, je ne comprends pas très bien pourquoi le super appel résout le problème.onActivityResult()
Est appelé avantonResume()
, donc il ne devrait toujours pas être autorisé à afficher une boîte de dialogue de fragment.)la source
J'obtenais cette exception lorsque j'appuyais sur le bouton de retour pour annuler le sélecteur d'intention sur l'activité de mon 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. J'espère que cela aide.
la source
Je pense que l'utilisation
transaction.commitAllowingStateLoss();
n'est pas la meilleure solution. Cette exception sera levée lorsque la configuration de l'activité a changé et fragmentéonSavedInstanceState()
est appelé, puis votre méthode de rappel asynchrone essaie de valider le fragment.Une solution simple pourrait être de vérifier si l'activité change de configuration ou non
par exemple vérifier
isChangingConfigurations()
c'est à dire
if(!isChangingConfigurations()) { //commit transaction. }
Consultez également ce lien
la source
La solution la plus simple et la plus simple que j'ai trouvée dans mon cas était probablement d'éviter de faire sauter le fragment incriminé de la pile en réponse au résultat de l'activité. Donc, changer cet appel dans mon
onActivityResult()
:pour ça:
aidé dans mon cas.
la source
Si vous effectuez une FragmentTransaction dans onActivityResult, ce que vous pouvez faire, 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.
la source
Courtoisie: Solution pour IllegalStateException
Ce problème m'ennuyait depuis longtemps mais heureusement, je suis venu avec une solution concrète. Une explication détaillée est ici .
L'utilisation de commitAllowStateloss () pourrait empêcher cette exception mais entraînerait des irrégularités d'interface utilisateur.Jusqu'à présent, nous avons compris que IllegalStateException se produit lorsque nous essayons de valider un fragment après la perte de l'état Activity - nous devons donc simplement retarder la transaction jusqu'à ce que l'état soit restauré. .Cela peut être fait simplement comme ceci
Déclarez deux variables booléennes privées
Maintenant, dans onPostResume () et onPause, nous définissons et désactivons notre variable booléenne isTransactionSafe. L'idée est de marquer la trasnsaction en toute sécurité uniquement lorsque l'activité est au premier plan, de sorte qu'il n'y a aucune chance de perte d'état.
-Ce que nous avons fait jusqu'à présent permettra d'économiser de IllegalStateException mais nos transactions seront perdues si elles sont effectuées après que l'activité se déplace en arrière-plan, un peu comme commitAllowStateloss (). Pour vous aider, nous avons la variable booléenne isTransactionPending
la source
Les transactions de fragments ne doivent pas être exécutées après
Activity.onStop()
! Vérifiez que vous n'avez aucun rappel qui pourrait exécuter la transaction aprèsonStop()
. Il est préférable de corriger la raison au lieu d'essayer de contourner le problème avec des approches comme.commitAllowingStateLoss()
la source
À partir de la bibliothèque de support version 24.0.0, vous pouvez appeler la
FragmentTransaction.commitNow()
méthode qui valide cette transaction de manière synchrone au lieu d'appelercommit()
suivi deexecutePendingTransactions()
. Comme le dit la documentation , cette approche est encore meilleure:la source
Chaque fois que vous essayez de charger un fragment dans votre activité, assurez-vous que l'activité est en cours de reprise et ne va pas se mettre en pause. Dans un état 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 le fragment
ou
Créez un booléen et vérifiez si l'activité ne va pas être interrompue
puis lors du chargement de la vérification des fragments
la source
Pour contourner ce problème, nous pouvons utiliser le composant d'architecture de navigation , qui a été introduit dans Google I / O 2018. Le composant d'architecture de navigation simplifie la mise en œuvre de la navigation dans une application Android.
la source
En ce qui concerne @Anthonyeef grande réponse, voici un exemple de code en Java:
la source
Si vous avez un crash avec la méthode popBackStack () ou popBackStackImmediate (), veuillez essayer fixt avec:
Cela fonctionne aussi pour moi.
la source
Dans mon cas, j'ai eu cette erreur dans une méthode de remplacement appelée onActivityResult. Après avoir creusé, je viens de comprendre que je devais peut-être appeler « super » avant.
Je l'ai ajouté et ça a juste fonctionné
Peut-être que vous avez juste besoin d'ajouter un «super» sur tout remplacement que vous faites avant votre code.
la source
Extension Kotlin
Usage:
la source
fragment: Fragment
? Oui, j'ai essayé cette variante, mais dans ce cas, un fragment serait créé dans tous les cas (même lorsque fragmentManager == null, mais je n'ai pas rencontré cette situation). J'ai mis à jour la réponse et changé null pour ajouter un tagaddToBackStack()
.Ce plantage est dû à une FragmentTransaction commise après que le cycle de vie de son activité a déjà été exécuté sur SaveInstanceState. Cela est souvent dû à la validation de FragmentTransactions à partir d'un rappel asynchrone. Consultez la ressource liée pour plus de détails.
Transactions de fragments et perte d'état d'activité
http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
la source
Ajoutez ceci dans votre activité
la source
J'ai également rencontré ce problème et un problème se produit 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 votreFragmentActivity
.la source
J'ai fini par créer un fragment de base et faire en sorte que tous les fragments de mon application l'étendent
Ensuite, lorsque j'essaie de montrer un fragment que j'utilise
showAllowingStateLoss
au lieu deshow
comme ça:
Je suis venu à cette solution à partir de ce PR: https://github.com/googlesamples/easypermissions/pull/170/files
la source
Une autre solution de contournement possible, dont je ne sais pas si cela aide dans tous les cas (origine ici ):
la source
Je sais qu'il y a une réponse acceptée par @Ovidiu Latcu mais après un certain temps, l'erreur persiste.
Crashlytics m'envoie toujours ce message d'erreur étrange.
Cependant, l'erreur ne se produit maintenant que sur la version 7+ (Nougat) Mon correctif consistait à utiliser commitAllowingStateLoss () au lieu de commit () au fragmentTransaction.
Ce message est utile pour commitAllowingStateLoss () et n'a plus jamais eu 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. codages heureux. <3 acclamations
la source