在完成AsyncTask后,如何处理解除DialogFragment(兼容性lib)的问题

时间:2012-05-25 20:11:58

标签: java android android-fragments

在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,是不是有这样的东西?

6 个答案:

答案 0 :(得分:22)

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

在这种情况下,您绝对不希望使用commitAllowingStateLoss()来避免异常。以此场景为例:

  1. 活动执行AsyncTaskAsyncTaskDialogFragment中显示onPreExecute(),并开始在后台线程上执行其任务。
  2. 用户点击“主页”,Activity停止并强制进入后台。系统决定设备的内存非常低,因此它决定它也应该销毁Activity
  3. AsyncTask完成,onPostExecute()被调用。在onPostExecute()内,您使用DialogFragment关闭commitAllowingStateLoss()以避免例外。
  4. 用户导航回ActivityFragmentManager将根据Activity的已保存状态恢复其片段的状态。保存状态在调用onSaveInstanceState()后不知道任何内容,因此即使{{1} DialogFragment,也不会记住解除DialogFragment的请求。已经完成了。
  5. 由于偶尔会发生类似这些奇怪的错误,因此使用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开发的讨论中,我无法在其他地方找到我的设计的某些方面。所以我对任何反馈感兴趣。

总之,我必须做的是:

  • onSaveInstanceState() - 如果SomeTask正在运行,我会使用一个简单的锁来阻止SomeTask在暂停期间退出doInBackground()。
  • onResume() - 我必须输入一个switch语句来处理不同的恢复情况。如果启动应用程序我什么都不做,因为没有暂停,如果在隐藏后重新启动或在配置更改后重新启动我释放锁定,以便保留的SomeTask实例可以从它停止的地方恢复,等等。
  • onDestroy() - 如果SomeTask正在运行,我会取消它。

我会把这个解决方案的代码片段放在原来的帖子里。

答案 5 :(得分:0)

getActivity().finish();中的{p> DialogFragment为我工作。