chr*_*ine 110 android dialog fragment
有些用户报告,如果他们使用通知栏中的快速操作,他们就会收到一个强制关闭.
我在通知中显示了一个快速操作,它调用了"TestDialog"类.在按下"贪睡"按钮后的TestDialog类中,我将显示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");
}
Run Code Online (Sandbox Code Playgroud)
错误是 *IllegalStateException: Can not perform this action after onSaveInstanceState*.
IllegarStateException被触发的代码行是:
snoozeDialog.show(fm, "snooze_dialog");
Run Code Online (Sandbox Code Playgroud)
该类正在扩展"FragmentActivity",而"SnoozeDialog"类正在扩展"DialogFragment".
以下是错误的完整堆栈跟踪:
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)
Run Code Online (Sandbox Code Playgroud)
我无法重现此错误,但我收到了很多错误报告.
任何人都可以帮助我如何解决这个错误?
Raf*_*ael 57
这是常见问题.我们通过重写show()并在DialogFragment扩展类中处理异常来解决这个问题
public class CustomDialogFragment extends DialogFragment {
@Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
} catch (IllegalStateException e) {
Log.d("ABSDIALOGFRAG", "Exception", e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,应用此方法不会更改DialogFragment.class的内部字段:
boolean mDismissed;
boolean mShownByMe;
Run Code Online (Sandbox Code Playgroud)
在某些情况下,这可能会导致意外结果.更好地使用commitAllowingStateLoss()而不是commit()
Pon*_*pat 24
这意味着你commit()(show()在DialogFragment的情况下)片段之后onSaveInstanceState().
Android将保存您的片段状态onSaveInstanceState().因此,如果片段状态commit()后onSaveInstanceState()碎片将丢失.
因此,如果Activity被杀死并稍后重新创建,则片段将不会添加到用户体验不佳的活动.这就是为什么Android不会不惜一切代价让国家损失的原因.
简单的解决方案是检查状态是否已保存.
boolean mIsStateAlreadySaved = false;
boolean mPendingShowDialog = false;
@Override
public void onResumeFragments(){
super.onResumeFragments();
mIsStateAlreadySaved = false;
if(mPendingShowDialog){
mPendingShowDialog = false;
showSnoozeDialog();
}
}
@Override
public void onPause() {
super.onPause();
mIsStateAlreadySaved = true;
}
private void showSnoozeDialog() {
if(mIsStateAlreadySaved){
mPendingShowDialog = true;
}else{
FragmentManager fm = getSupportFragmentManager();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(fm, "snooze_dialog");
}
}
Run Code Online (Sandbox Code Playgroud)
注意:onResumeFragments()将在片段恢复时调用.
小智 19
使用 Activity-KTX 的新生命周期范围就像以下代码示例一样简单:
lifecycleScope.launchWhenResumed {
showErrorDialog(...)
}
Run Code Online (Sandbox Code Playgroud)
此方法可以在 onStop() 之后直接调用,并且在返回时调用 onResume() 后将成功显示对话框。
huu*_*duy 15
private void showSnoozeDialog() {
FragmentManager fm = getSupportFragmentManager();
SnoozeDialog snoozeDialog = new SnoozeDialog();
// snoozeDialog.show(fm, "snooze_dialog");
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(snoozeDialog, "snooze_dialog");
ft.commitAllowingStateLoss();
}
Run Code Online (Sandbox Code Playgroud)
参考:链接
Den*_*ski 10
几天后,我想分享我的解决方案如何,我已经修复它,以示DialogFragment你应该重写show()它的方法和调用commitAllowingStateLoss()的Transaction对象.这是Kotlin的例子:
override fun show(manager: FragmentManager?, tag: String?) {
try {
val ft = manager?.beginTransaction()
ft?.add(this, tag)
ft?.commitAllowingStateLoss()
} catch (ignored: IllegalStateException) {
}
}
Run Code Online (Sandbox Code Playgroud)
如果对话框不是很重要(可以在应用程序关闭/不再查看时不显示它),请使用:
boolean running = false;
@Override
public void onStart() {
running = true;
super.onStart();
}
@Override
public void onStop() {
running = false;
super.onStop();
}
Run Code Online (Sandbox Code Playgroud)
并且只在我们运行时打开您的对话框(片段):
if (running) {
yourDialog.show(...);
}
Run Code Online (Sandbox Code Playgroud)
编辑,可能更好的解决方案:
在生命周期中调用onSaveInstanceState是不可预测的,我认为更好的解决方案是检查isSavedInstanceStateDone(),如下所示:
/**
* 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;
}
Run Code Online (Sandbox Code Playgroud)
如果您重写 show() 函数,请不要这样做:
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()
}
Run Code Online (Sandbox Code Playgroud)
它可能会导致错误的对话框状态
做就是了:
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()
}
}
Run Code Online (Sandbox Code Playgroud)
我已经遇到这个问题很多年了。
互联网上散布着成百上千(数百?数千?)关于此的讨论,并且其中的困惑和虚假信息似乎很多。
更糟糕的是,本着xkcd“ 14 standard”漫画的精神,我将自己的答案扔进了电话里。

在cancelPendingInputEvents(),commitAllowingStateLoss(),catch (IllegalStateException e),和类似的解决方案都显得残酷。
希望以下内容可以轻松地显示如何重现和解决问题:
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);
}
};
Run Code Online (Sandbox Code Playgroud)
请尝试使用FragmentTransaction而不是FragmentManager.我认为以下代码将解决您的问题.如果没有,请告诉我.
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(ft, "snooze_dialog");
Run Code Online (Sandbox Code Playgroud)
编辑:
请检查此链接.我认为它会解决你的疑问.
小智 5
使您的对话框片段对象成为全局对象并在 onPause() 方法中调用dismissAllowingStateLoss()
@Override
protected void onPause() {
super.onPause();
if (dialogFragment != null) {
dialogFragment.dismissAllowingStateLoss();
}
}
Run Code Online (Sandbox Code Playgroud)
不要忘记在片段中分配值并在按钮单击或任何地方调用 show() 。
虽然没有在任何地方正式提及,但我遇到过这个问题几次。根据我的经验,旧平台上支持片段的兼容性库存在问题,导致此问题。您可以使用普通的片段管理器 API 来测试它。如果没有任何效果,那么您可以使用普通对话框而不是对话框片段。
| 归档时间: |
|
| 查看次数: |
43966 次 |
| 最近记录: |