DialogFrag#show()来自片段抛出"IllegalStateException:onSaveInstanceState后无法执行此操作"

Wil*_*iam 14 android android-lifecycle android-fragments android-dialogfragment android-nested-fragment

为了清楚起见,我已经阅读了关于"IllegalStateException:在onSaveInstanceState之后无法执行此操作"的十几个SO问题,我已经阅读了Alex Lockwood的博客文章http://www.androiddesignpatterns.com/2013/08/片段事务提交状态-loss.html

所以我不是盲目地问这个.

我有一个非常简单的用例案例,不涉及AsyncTask或任何后台处理.

我有一个包含按钮的片段.在按钮的onClickListener上,我创建了一个DialogFragment并显示它.

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);
            }
        });
}
Run Code Online (Sandbox Code Playgroud)

[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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

[堆栈跟踪]

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)
Run Code Online (Sandbox Code Playgroud)

NB片段和DialogFragment都来自support-v4:21.0.0

我在一系列运行4.4的设备上看到了这一点.但是在运行5.0的Nexus 7上至少发生过一次.

我自己无法复制这个.甚至没有通过在onClick中引入人工延迟并尝试旋转,返回,回家应用程序.

因此,直接从UI线程创建和提交FragmentTransaction(for DialogFrag#show())onClick(),如何Fragment已经过去了onSaveInstanceState()?

这是否意味着我需要在每个用户输入的开头检查活动生命周期的状态?- 非常糟糕(Lifecycle意味着要为我处理.如果活动已经过去,我不应该接收用户输入onPause())

这是否意味着我需要在执行用户输入期间检查每个语句之前的活动生命周期的状态? - 坏了!!

我该怎么做才能阻止这种情况发生?

更多信息:

在野外跑了好几天后,我可以断然说这getChildFragmentManager()不是解决方案.

以下Android版本发生故障:

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

Wil*_*iam 4

好的,据我所知,这是AOSP 中的一个错误,因为我也从纯 Android 堆栈中看到了一个这样的实例(即我的代码中根本没有任何内容)。

因此,看起来 Activity/Fragment 生命周期中存在线程问题,其中 UI 线程可以在Activity/Fragment 已保存其状态后优先响应按钮单击。

到目前为止,我的工作已 100% 成功,即捕获 IllegalStateException 并安排下次 Activity/Fragment 使用 PauseHandler 激活时的对话框显示/sf/answers/1772563131/