DialogFrag#show() from a Fragment throwing “IllegalStateException: Can not perform this action after onSaveInstanceState”

守給你的承諾、 提交于 2019-11-27 14:47:37

问题


Just to be clear, I have read the dozen top SO questions on "IllegalStateException: Can not perform this action after onSaveInstanceState" and I have read Alex Lockwood's blog post on the issue http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html

So I'm not asking this blindly.

I have a very simple use case case that doesn't involve AsyncTask or any background processing.

I have a Fragment that contains a button. On the onClickListener for the button, I create a DialogFragment and show it.

public final class OverviewFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.overview_fragment, container, false);

        startNewGameButton = (Button) view.findViewById(R.id.buttonNewGame);
        startNewGameButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final NewGameFragment dialogFrag = NewGameFragment.create(getApplication());
                dialogFrag.show(getFragmentManager(), NewGameFragment.FRAGMENT_TAG);
            }
        });
}

[NewGameFragment]

public final class NewGameFragment extends DialogFragment {

    public static final String FRAGMENT_TAG = "NewGameFragment";

    private static final String MESSAGE = "message";

    public static NewGameFragment create(Context context) {
        final AppsPreferences prefs = new AppPreferences(context);
        final int startOption = prefs.getGameStartOption();

        final Bundle bundle = new Bundle();
        bundle.putString(MESSAGE, getMessage(context, startOption));

        final NewGameFragment fragment = new NewGameFragment();
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public final Dialog onCreateDialog(Bundle savedInstanceState) {
        final String message = getArguments().getString(MESSAGE);

        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
            .setTitle(R.string.progress_startGame_title)
            .setMessage(message);

        builder.setPositiveButton(R.string.progress_startGame_raceButton, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                new RaceAction().execute();
            }
        });
        builder.setNegativeButton(R.string.progress_startGame_eventButton, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                new EventAction().execute();
            }
        });

        final Dialog dialog = builder.create();
        dialog.setCanceledOnTouchOutside(false); // Whether clicking outside the dialog closes the dialog.
        return dialog;
    }
  }

[Stacktrace]

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.void checkStateLoss()(SourceFile:1365)
at android.support.v4.app.FragmentManagerImpl.void enqueueAction(java.lang.Runnable,boolean)(SourceFile:1383)
at android.support.v4.app.BackStackRecord.int commitInternal(boolean)(SourceFile:636)
at android.support.v4.app.BackStackRecord.int commit()(SourceFile:615)
at android.support.v4.app.DialogFragment.void show(android.support.v4.app.FragmentManager,java.lang.String)(SourceFile:138)
at au.com.xandar.thegame.overview.OverviewFragment$1.void onClick(android.view.View)(SourceFile:160)
at android.view.View.performClick(View.java:4162)
at android.view.View$PerformClick.run(View.java:17082)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4867)
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:1007)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
at dalvik.system.NativeStart.main(Native Method)

NB the Fragment and DialogFragment both come from support-v4:21.0.0

I am seeing this on a range of devices running 4.4. But at least one instance has occurred on a Nexus 7 running 5.0.

I have not been able to replicate this myself. Not even by introducing an artificial delay into the onClick and attempting to rotate, back, home the app.

So since the FragmentTransaction (for DialogFrag#show()) is being created and committed on the UI Thread directly from onClick(), how can the Fragment already have proceeded past onSaveInstanceState()?

Does it mean that I need to check the state of the Activity Lifecycle at the start of every user input? - very bad (the Lifecycle is meant to be handling that for me. I shouldn't be receiving user input if the Activity is already past onPause())

Does it mean I need to check the state of the Activity Lifecycle prior to every statement during execution of user input? - broken bad !!

What can I do to stop this occurring?

Further info:

After running in the wild for several days I can categorically say that getChildFragmentManager() is not the solution.

Failure occurs for the following Android versions:

  • 4.4.2 90%
  • 4.4.4 5%
  • 5.0 5%

回答1:


OK, as far as I can tell this is a bug in the AOSP as I have also seen an instance of this from pure Android stack (ie nothing of my code at all).

So it looks like there is a threading issue in the Activity/Fragment lifecycle in which a UI thread can get priority to respond to a button click AFTER the Activity/Fragment has already saved it's state.

My work around which has been 100% successful so far is to catch the IllegalStateException and schedule the dialog show for the next time the Activity/Fragment becomes active using a PauseHandler https://stackoverflow.com/a/25322330/493682



来源:https://stackoverflow.com/questions/27329913/dialogfragshow-from-a-fragment-throwing-illegalstateexception-can-not-perfo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!