配置变更时在Runnable中调用DialogFragment触发IllegalStateException及解决
问题重现
你遇到的场景非常典型:这段延迟执行的代码在正常状态下能正常弹出对话框,但当设备发生配置变更(比如屏幕旋转)时,就会抛出IllegalStateException,错误日志提示:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: ru.scbit.yarovoe, PID: 21960
java.lang.IllegalStateException: Can not perform this action after onSaveIns...
触发异常的代码如下:
timer.schedule(new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { new SaveDataDialog().show(getFragmentManager(), "save-data-dialoge"); } }); } }, 3000);
问题根源
当设备发生配置变更时,系统会销毁当前Activity并重建新实例。但你的TimerTask是和原Activity绑定的,它不会随原Activity销毁自动取消。当延迟时间到了之后,任务依然会尝试在原Activity的上下文里调用show()方法——而此时原Activity已经执行了onSaveInstanceState(),系统已经保存了Activity状态,不允许再执行修改Fragment栈的操作(比如显示DialogFragment),这就触发了异常。
已验证的解决方案
你提到在onPause()中取消定时器后问题解决,这完全正确!因为onPause()是配置变更触发销毁前一定会执行的生命周期方法,在这里取消定时器可以彻底阻止后续任务执行,避免在Activity状态不合法时操作Fragment。
具体实现可以参考:
// 在Activity中持有Timer的引用 private Timer timer; // 启动定时器的逻辑 timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { new SaveDataDialog().show(getFragmentManager(), "save-data-dialoge"); } }); } }, 3000); // 在onPause中取消定时器并释放引用 @Override protected void onPause() { super.onPause(); if (timer != null) { timer.cancel(); timer = null; // 避免内存泄漏 } }
额外优化建议
如果需要在配置变更后继续执行延迟任务,可以用ViewModel托管定时器逻辑——ViewModel在配置变更时不会被销毁,能保留任务状态,在新Activity重建后继续执行或重新触发。也可以使用LifecycleObserver监听Activity生命周期,自动管理任务启停,让代码更健壮。
内容的提问来源于stack exchange,提问作者Igor M.

