157 votes

Lors de l'affichage du dialogue, j'obtiens "Can not perform this action after onSaveInstanceState".

Certains utilisateurs signalent que s'ils utilisent l'action rapide dans la barre de notification, ils obtiennent une fermeture forcée.

Je montre une action rapide dans la notification qui appelle la "TestDialog" classe. Dans la classe TestDialog, après avoir appuyé sur le bouton "snooze", je vais afficher le SnoozeDialog.

private View.OnClickListener btnSnoozeOnClick() {
    return new View.OnClickListener() {

        public void onClick(View v) {
            showSnoozeDialog();
        }
    };
}

private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    snoozeDialog.show(fm, "snooze_dialog");
}

L'erreur est *IllegalStateException: Can not perform this action after onSaveInstanceState*.

La ligne de code où l'exception IllegarStateException est déclenchée est la suivante :

snoozeDialog.show(fm, "snooze_dialog");

La classe étend "FragmentActivity" et la classe "SnoozeDialog" étend "DialogFragment".

Voici la trace complète de l'erreur :

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)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
at com.test.testing.TestDialog.f(TestDialog.java:538)
at com.test.testing.TestDialog.e(TestDialog.java:524)
at com.test.testing.TestDialog.d(TestDialog.java:519)
at com.test.testing.g.onClick(TestDialog.java:648)
at android.view.View.performClick(View.java:3620)
at android.view.View$PerformClick.run(View.java:14292)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)

Je ne peux pas reproduire cette erreur, mais je reçois de nombreux rapports d'erreur.

Quelqu'un peut-il m'aider à réparer cette erreur ?

4voto

Utiliser ce code

FragmentTransaction ft = fm.beginTransaction();
        ft.add(yourFragment, "fragment_tag");
        ft.commitAllowingStateLoss();

au lieu de

yourFragment.show(fm, "fragment_tag");

3voto

sim Points 536

De nombreuses vues postent des événements de haut niveau, tels que les gestionnaires de clics, dans la file d'attente des événements pour une exécution différée. Le problème est donc que "onSaveInstanceState" a déjà été appelé pour l'activité mais que la file d'attente des événements contient l'événement "click" différé. Par conséquent, lorsque cet événement est envoyé à votre gestionnaire

at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)

et votre code fait show l'exception IllegalStateException est levée.

La solution la plus simple est de nettoyer la file d'attente d'événements, en onSaveInstanceState

protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // ..... do some work
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            findViewById(android.R.id.content).cancelPendingInputEvents();
        }
}

1voto

Dalvinder Singh Points 441

Bien que cela ne soit mentionné officiellement nulle part, j'ai été confronté à ce problème à plusieurs reprises. D'après mon expérience, il y a un problème dans la bibliothèque de compatibilité qui prend en charge les fragments sur les anciennes plateformes, ce qui cause ce problème. Vous pouvez le tester en utilisant l'API normale du gestionnaire de fragments. Si rien ne fonctionne, vous pouvez utiliser le dialogue normal au lieu du fragment de dialogue.

1voto

Gil SH Points 706
  1. Ajoutez cette classe à votre projet : (doit être en Android.support.v4.app paquet)

    package android.support.v4.app;

    /** * Created by Gil on 8/16/2017. */

    public class StatelessDialogFragment extends DialogFragment { /** * Display the dialog, adding the fragment using an existing transaction and then committing the * transaction whilst allowing state loss.
    * * I would recommend you use {@link #show(FragmentTransaction, String)} most of the time but * this is for dialogs you reallly don't care about. (Debug/Tracking/Adverts etc.) * * @param transaction * An existing transaction in which to add the fragment. * @param tag * The tag for this fragment, as per * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. * @return Returns the identifier of the committed transaction, as per * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. * @see StatelessDialogFragment#showAllowingStateLoss(FragmentManager, String) */ public int showAllowingStateLoss(FragmentTransaction transaction, String tag) { mDismissed = false; mShownByMe = true; transaction.add(this, tag); mViewDestroyed = false; mBackStackId = transaction.commitAllowingStateLoss(); return mBackStackId; }

    /\*\*
     \* Display the dialog, adding the fragment to the given FragmentManager. This is a convenience
     \* for explicitly creating a transaction, adding the fragment to it with the given tag, and
     \* committing it without careing about state. This does _not_ add the transaction to the
     \* back stack. When the fragment is dismissed, a new transaction will be executed to remove it
     \* from the activity.  
     \*
     \* I would recommend you use {@link #show(FragmentManager, String)} most of the time but this is
     \* for dialogs you reallly don't care about. (Debug/Tracking/Adverts etc.)
     \*
     \*
     \* @param manager
     \*            The FragmentManager this fragment will be added to.
     \* @param tag
     \*            The tag for this fragment, as per
     \*            {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
     \* @see StatelessDialogFragment#showAllowingStateLoss(FragmentTransaction, String)
     \*/
    public void showAllowingStateLoss(FragmentManager manager, String tag)
    {
        mDismissed = false;
        mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commitAllowingStateLoss();
    }

    }

  2. Étendre le site StatelessDialogFragment au lieu de DialogFragment

  3. Utilisez la méthode showAllowingStateLoss au lieu de montrer

  4. Profitez-en ;)

1voto

J'ai trouvé une solution élégante à ce problème en utilisant la réflexion. Le problème de toutes les solutions ci-dessus est que les champs mDismissed et mShownByMe ne changent pas d'état.

Il suffit de surcharger la méthode "show" dans votre propre fragment de dialogue personnalisé de la feuille de fond comme l'exemple ci-dessous (Kotlin)

override fun show(manager: FragmentManager, tag: String?) {
        val mDismissedField = DialogFragment::class.java.getDeclaredField("mDismissed")
        mDismissedField.isAccessible = true
        mDismissedField.setBoolean(this, false)

        val mShownByMeField = DialogFragment::class.java.getDeclaredField("mShownByMe")
        mShownByMeField.isAccessible = true
        mShownByMeField.setBoolean(this, true)

        manager.beginTransaction()
                .add(this, tag)
                .commitAllowingStateLoss()
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X