如何处理在 AsyncTask 完成后关闭 DialogFragment(兼容库)

2022-09-04 05:29:40

有许多关于如何在 AsyncTask 期间处理配置更改的帖子,但是我没有找到一个关于在 AsyncTask 完成并尝试关闭 DialogFragment(兼容性库)时处于后台(onPause())的应用程序的明确解决方案。

这就是问题所在,如果我有一个AsyncTask正在运行,它应该在onPostExecute()中关闭DialogFragment,如果应用程序在尝试关闭DialogFragment时处于后台,我会收到一个非法StateException。

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试图解雇SomeDialog()时,我得到一个非法状态异常。

05-25 16:36:02.237: E/AndroidRuntime(965): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

我看到的所有帖子似乎都指向一些笨拙的方向,并提出了详细的解决方法。难道没有一些机器人方法来处理这个问题吗?如果它是对话框而不是对话框碎片,则活动的 dismissDialog() 将正确处理它。如果它是一个真正的DialogFragment而不是来自ACP的DialogFragment,那么discolenAllowingStateLoss()将处理它。ACP版本的DialogFragment不是有这样的东西吗?


答案 1

片段被保存为每个活动状态的一部分,因此在技术上调用后执行事务是没有意义的。onSaveInstanceState()

在这种情况下,您绝对不想使用来避免异常。请考虑以下方案作为示例:commitAllowingStateLoss()

  1. 活动执行 .将显示 in 并开始在后台线程上执行其任务。AsyncTaskAsyncTaskDialogFragmentonPreExecute()
  2. 用户单击“主页”,则停止并强制进入后台。系统决定该设备的内存不足,因此它决定它也应该销毁。ActivityActivity
  3. 完成并被调用。在内部,您可以消除使用以避免异常。AsyncTaskonPostExecute()onPostExecute()DialogFragmentcommitAllowingStateLoss()
  4. 用户导航回 .将根据 的保存状态恢复其片段的状态。保存的状态在调用后不知道任何内容,因此不会记住关闭的请求,即使已完成,也会恢复。ActivityFragmentManagerActivityonSaveInstanceState()DialogFragmentDialogFragmentAsyncTask

由于偶尔会发生这些奇怪的错误,因此通常不是避免此异常的好主意。由于回调方法(为响应完成其工作的后台线程而调用)与生命周期方法(由系统服务器进程调用以响应系统范围的外部事件(如设备入睡或内存不足)完全无关,因此处理这些情况需要您执行一些额外的工作。当然,这些错误非常罕见,保护您的应用程序免受它们侵害通常不会是Play商店中1星评级和5星级评级之间的区别......但它仍然是需要注意的。commitAllowingStateLoss()AsyncTaskActivity

希望这至少有一定的道理。另外,请注意,s 也作为 s 状态的一部分存在,因此,尽管使用普通的 old 可能会避免异常,但您基本上会遇到相同的问题(即,在稍后恢复 '的状态时,消除 不会被记住)。DialogActivityDialogDialogActivity

坦率地说,最好的解决方案是避免在整个期间显示对话。一个更加用户友好的解决方案是在 中显示不确定的进度微调器(例如G +和Gmail应用程序)。在用户界面中引起重大更改以响应异步回调对用户体验不利,因为这是意外的,并且会突然将用户从他们正在做的事情中拉出来。AsyncTaskActionBar

有关详细信息,请参阅有关该主题的这篇博客文章


答案 2

要解决非法状态异常问题,并实质上实现解雇允许StateLoss()可以使用以下内容完成。

getFragmentManager().beginTransaction().remove(someDialog).commitAllowingStateLoss();

这应该可以在没有黑客代码的情况下解决问题。如果您有线程使用 dialog.show();这也可能导致非法状态异常

getFragmentManager().beginTransaction().add(someDialog).commitAllowingStateLoss();


鉴于海报问题,@joneswah是正确的。如果您使用的是支持库,请替换
getFragmentManager()

getSupportFragmentManager()


对于未来的Google员工:@Alex Lockwood对此解决方案提出了良好且有效的担忧。该解决方案确实解决了错误,并且在大多数情况下都有效,但从UX的角度来看,该解决方案暗示了原始问题中的方法存在问题。

活动应假定异步任务可能无法完成,并且它不会在PostExecute() 上执行。无论启动什么 UI 操作(即微调器,理想情况下不是对话框)来通知用户异步操作,都应该有规定在超时时或通过跟踪状态并签入 onRestore/onResue 类型生命周期事件来自动停止,以确保 UI 正确更新。服务也可能值得调查。


推荐