在AsyncTask期间有很多关于如何处理配置更改的帖子,但是当AsyncTask完成并尝试解除DialogFragment时,我找不到任何关于后台应用程序(onPause())的明确解决方案(兼容性)图书馆)。
问题是,如果我运行的AsyncTask应该解除onPostExecute()中的DialogFragment,如果应用程序在后台尝试关闭DialogFragment时,我会收到IllegalStateException。
private static class SomeTask extends AsyncTask<Void, Void, Boolean> {
public SomeTask(SomeActivity tActivity)
{
mActivity = tActivity;
}
private SomeActivity mActivity;
/** Set the view during/after config change */
public void setView(Activity tActivity) {
mActivity tActivity;
}
@Override
protected Boolean doInBackground(Void... tParams) {
try {
//simulate some time consuming process
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException ignore) {}
return true;
}
@Override
protected void onPostExecute(Boolean tRouteFound) {
mActivity.dismissSomeDialog();
}
}
活动如下所示:
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
public class SomeActivity extends FragmentActivity {
public void someMethod() {
...
displaySomeDialog();
new SomeTask(this).execute();
...
}
public void displaySomeDialog() {
DialogFragment someDialog = new SomeDialogFragment();
someDialog.show(getFragmentManager(), "dialog");
}
public void dismissSomeDialog() {
SomeDialogFragment someDialog = (SomeDialogFragment) getFragmentManager().findFragmentByTag("dialog");
someDialog.dismiss();
}
....
}
正常工作,除非SomeTask仍在运行,否则应用会切换到后台。在这种情况下,当SomeTask尝试dismissSomeDialog()时,我得到一个IllegalStateException。
05-25 16:36:02.237: E/AndroidRuntime(965): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
我看过的所有帖子似乎都指出了一些复杂的方法,并提供了精心设计的解决方法。是不是有一些android处理方式?如果它是Dialog而不是DialogFragment,那么Activity的dismissDialog()会正确处理它。如果它是一个真正的DialogFragment而不是ACP中的一个,那么dismissAllowingStateLoss()会处理它。对于ACP版本的DialogFragment,是不是有这样的东西?
答案 0 :(得分:22)
片段被保存为每个Activity状态的一部分,因此在技术上调用onSaveInstanceState()
之后执行事务是没有意义的。
在这种情况下,您绝对不希望使用commitAllowingStateLoss()
来避免异常。以此场景为例:
AsyncTask
。 AsyncTask
在DialogFragment
中显示onPreExecute()
,并开始在后台线程上执行其任务。Activity
停止并强制进入后台。系统决定设备的内存非常低,因此它决定它也应该销毁Activity
。AsyncTask
完成,onPostExecute()
被调用。在onPostExecute()
内,您使用DialogFragment
关闭commitAllowingStateLoss()
以避免例外。Activity
。 FragmentManager
将根据Activity
的已保存状态恢复其片段的状态。保存状态在调用onSaveInstanceState()
后不知道任何内容,因此即使{{1} DialogFragment
,也不会记住解除DialogFragment
的请求。已经完成了。由于偶尔会发生类似这些奇怪的错误,因此使用AsyncTask
来避免此异常通常不是一个好主意。因为commitAllowingStateLoss()
回调方法(响应后台线程完成其工作而调用)与AsyncTask
生命周期方法完全无关(系统服务器进程响应系统调用它们)整个外部事件,例如设备入睡或内存不足,处理这些情况需要您做一些额外的工作。当然,这些错误是非常罕见的,保护你的应用程序对他们来说往往不是1星评级和游戏商店的5星评级之间的差异......但它仍然需要注意。
希望至少在某种程度上有所作为。另请注意,Activity
也作为Dialog
s状态的一部分存在,因此尽管使用普通的旧Activity
可能会避免异常,但您基本上会遇到同样的问题(即解雇)当Dialog
的状态稍后恢复时,Dialog
将不会被记住。
坦率地说,最好的解决方案是避免在Activity
的整个过程中显示对话框。一个更加用户友好的解决方案是在AsyncTask
中显示一个不确定的进度微调器(例如G +和Gmail应用程序)。导致用户界面发生重大转变以响应异步回调对用户体验不利,因为它是意外的,并且突然使用户不再正在做他们正在做的事情。
有关详情,请参阅主题this blog post。
答案 1 :(得分:16)
要解决非法状态异常问题并且基本上可以使用以下方法实现dismissAllowingStateLoss()。
getFragmentManager().beginTransaction().remove(someDialog).commitAllowingStateLoss();
这应该可以在没有hacky代码的情况下解决问题。如果线程使用dialog.show()通过处理程序与UI线程进行通信,则也可以应用于show。这也可能导致非法状态异常
getFragmentManager().beginTransaction().add(someDialog).commitAllowingStateLoss();
<小时/> 鉴于海报问题,@ joneswah是正确的。 如果您使用的是支持库,请替换
getFragmentManager()
与
getSupportFragmentManager()
<小时/> 对于未来的Google员工: @Alex Lockwood对此解决方案提出了良好而有效的顾虑。该解决方案确实解决了错误,并且在大多数情况下都能正常工作,但从UX的角度来看暗示原始问题中的方法存在问题。
活动应该假定异步任务可能无法完成,并且它不会执行onPostExecute()。无论UI动作(即,微调器,理想情况下不是对话框)开始通知用户异步操作,都应该有超时或跟踪状态自动停止并检查onRestore / onResume类型生命周期事件以确保UI已正确更新。服务也可能值得调查。
答案 2 :(得分:3)
如果onPostExecute()要更新UI,您应该在onPause()中取消AsyncTask。您的活动暂停时不应尝试更新UI。
EG。在你的onPause():
if (task != null) task.cancel(true);
如果您希望任务的更改持续到下一次,则将数据/更改存储在doInBackground()中,然后在您的活动/片段/对话框恢复时更新UI。
如果您不希望任务中的更改持续存在,则在onPostExecute()之前不要存储更改
答案 3 :(得分:0)
当Android停止您的应用时,因为用户点击了后面或主页按钮,您的对话框就会关闭。通常的诀窍是保留onStop()/ onStart()之间的对话框。因此,除非您需要做的不仅仅是关闭对话框,否则我会说不要担心它。
编辑:在托管对话框的活动中,如果对话框仍在onStop()中打开,您可能仍希望关闭该对话框。这有助于防止内存泄漏。但这不需要从AsyncTask触发。
就像我上面说的那样,问题是当你再次点击onStart()并且你的AsyncTask尚未完成时会发生什么。您需要找出一种方法来确定并在需要时重新打开该对话框。
答案 4 :(得分:0)
经过多次重新设计后,我终于找到了一种似乎有效的方法。但是,在Android开发的讨论中,我无法在其他地方找到我的设计的某些方面。所以我对任何反馈感兴趣。
总之,我必须做的是:
我会把这个解决方案的代码片段放在原来的帖子里。
答案 5 :(得分:0)
getActivity().finish();
中的{p> DialogFragment
为我工作。