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 ?

13voto

Frank Points 1074

Si la boîte de dialogue n'est pas vraiment importante (il n'y a pas de problème à ne pas la montrer lorsque l'application est fermée ou n'est plus visible), utilisez :

boolean running = false;

@Override
public void onStart() {
    running = true;
    super.onStart();
}

@Override
public void onStop() {
    running = false;
    super.onStop();
}

Et n'ouvrez votre dialogue (fragment) que lorsque nous sommes en cours d'exécution :

if (running) {
    yourDialog.show(...);
}

EDIT, PROBABLEMENT UNE MEILLEURE SOLUTION :

L'endroit où onSaveInstanceState est appelé dans le cycle de vie est imprévisible, je pense qu'une meilleure solution est de vérifier sur isSavedInstanceStateDone() comme ceci :

/**
 * True if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
private boolean savedInstanceStateDone;

@Override
protected void onResume() {
    super.onResume();

    savedInstanceStateDone = false;
}

@Override
protected void onStart() {
    super.onStart();

    savedInstanceStateDone = false;
}

protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    savedInstanceStateDone = true;
}

/**
 * Returns true if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
public boolean isSavedInstanceStateDone() {
    return savedInstanceStateDone;
}

12voto

swooby Points 437

Je suis confronté à ce problème depuis des années.
Les Internets sont jonchés de dizaines (centaines ? milliers ?) de discussions à ce sujet, et la confusion et la désinformation y sont légion.
Pour aggraver la situation, et dans l'esprit de la bande dessinée xkcd "14 standards", je lance ma réponse dans l'arène.
xkcd 14 standards

El cancelPendingInputEvents() , commitAllowingStateLoss() , catch (IllegalStateException e) et les solutions similaires semblent toutes atroces.

J'espère que ce qui suit montre facilement comment reproduire et résoudre le problème :

private static final Handler sHandler = new Handler();
private boolean mIsAfterOnSaveInstanceState = true;

@Override
protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    mIsAfterOnSaveInstanceState = true; // <- To repro, comment out this line
}

@Override
protected void onPostResume()
{
    super.onPostResume();
    mIsAfterOnSaveInstanceState = false;
}

@Override
protected void onResume()
{
    super.onResume();
    sHandler.removeCallbacks(test);
}

@Override
protected void onPause()
{
    super.onPause();
    sHandler.postDelayed(test, 5000);
}

Runnable test = new Runnable()
{
    @Override
    public void run()
    {
        if (mIsAfterOnSaveInstanceState)
        {
            // TODO: Consider saving state so that during or after onPostResume a dialog can be shown with the latest text
            return;
        }

        FragmentManager fm = getSupportFragmentManager();
        DialogFragment dialogFragment = (DialogFragment) fm.findFragmentByTag("foo");
        if (dialogFragment != null)
        {
            dialogFragment.dismiss();
        }

        dialogFragment = GenericPromptSingleButtonDialogFragment.newInstance("title", "message", "button");
        dialogFragment.show(fm, "foo");

        sHandler.postDelayed(test, 5000);
    }
};

8voto

Si vous surchargez la fonction show(), NE LE FAITES PAS :

override fun show(manager: FragmentManager, tag: String?) {
    // mDismissed = false; is removed -> lead to wrong state
    // mShownByMe = true; is removed -> lead to wrong state
    val ft = manager.beginTransaction()
    ft.add(this, tag)
    ft.commitAllowingStateLoss()
}

Cela peut conduire à un mauvais état de dialogue

Fais-le :

override fun show(manager: FragmentManager, tag: String?) {
    try {
        super.show(manager, tag)
    } catch (e: Exception) {
        val ft = manager.beginTransaction()
        ft.add(this, tag)
        ft.commitAllowingStateLoss()
    }
}

7voto

Rohit Rajpal Points 96

Rendez votre objet fragment de dialogue global et appelez dismissAllowingStateLoss() dans la méthode onPause().

@Override
protected void onPause() {
    super.onPause();

    if (dialogFragment != null) {
        dialogFragment.dismissAllowingStateLoss();
    }
}

N'oubliez pas d'assigner une valeur dans le fragment et d'appeler show() au clic du bouton ou à tout autre moment.

6voto

RIJO RV Points 458

Essayez d'utiliser FragmentTransaction au lieu de FragmentManager. Je pense que le code ci-dessous résoudra votre problème. Si ce n'est pas le cas, veuillez me le faire savoir.

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(ft, "snooze_dialog");

EDIT :

Transaction par fragments

Veuillez consulter ce lien. Je pense qu'il répondra à vos questions.

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