来自 Fragment 的 DialogFrag#show() 抛出“IllegalStateException:在 onSaveInstanceState 之后无法执行此操作"; [英] DialogFrag#show() from a Fragment throwing "IllegalStateException: Can not perform this action after onSaveInstanceState"
问题描述
为了清楚起见,我已经阅读了关于IllegalStateException: Can not perform this action after onSaveInstanceState"的十几个顶级 SO 问题,并且我已经阅读了 Alex Lockwood 关于该问题的博文 http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
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.
我有一个非常简单的用例,它不涉及 AsyncTask 或任何后台处理.
I have a very simple use case case that doesn't involve AsyncTask or any background processing.
我有一个包含按钮的片段.在按钮的 onClickListener 上,我创建了一个 DialogFragment 并显示它.
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);
}
});
}
[新游戏片段]
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;
}
}
[堆栈跟踪]
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)
注意 Fragment 和 DialogFragment 都来自 support-v4:21.0.0
NB the Fragment and DialogFragment both come from support-v4:21.0.0
我在一系列运行 4.4 的设备上看到了这一点.但至少有一个实例发生在运行 5.0 的 Nexus 7 上.
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.
我自己无法复制这个.甚至不会通过在 onClick 中引入人为延迟并尝试旋转、返回、返回应用程序.
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.
因此,由于 FragmentTransaction
(对于 DialogFrag#show()
)是从 onClick()
直接在 UI 线程上创建和提交的,Fragment
怎么可能已经超过了onSaveInstanceState()
?
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()
?
这是否意味着我需要在每次用户输入开始时检查 Activity Lifecycle 的状态?- 非常糟糕(生命周期旨在为我处理.如果 Activity 已经过去了,我不应该接收用户输入 onPause()
)
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?
更多信息:
在野外跑了几天后,我可以断然说 getChildFragmentManager()
不是解决方案.
After running in the wild for several days I can categorically say that getChildFragmentManager()
is not the solution.
以下 Android 版本失败:
Failure occurs for the following Android versions:
- 4.4.2 90%
- 4.4.4 5%
- 5.0 5%
推荐答案
好的,据我所知这是一个 错误 在 AOSP 中,因为我也从纯 Android 堆栈中看到了这个实例(即根本没有我的代码).
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).
所以看起来 Activity/Fragment 生命周期中存在线程问题,其中 UI 线程可以优先响应按钮点击AFTER Activity/Fragment 已经保存了它的状态.
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.
我的工作到目前为止 100% 成功是捕获 IllegalStateException 并在下次活动/片段使用 PauseHandler https://stackoverflow.com/a/25322330/493682
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
这篇关于来自 Fragment 的 DialogFrag#show() 抛出“IllegalStateException:在 onSaveInstanceState 之后无法执行此操作";的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!