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

Chu*_*ger 13 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();  
    }

}
Run Code Online (Sandbox Code Playgroud)

活动看起来像这样:

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();
    }

    ....

}
Run Code Online (Sandbox Code Playgroud)

工作正常,除非SomeTask仍在运行,应用程序切换到后台.在这种情况下,当SomeTask尝试dismissSomeDialog()时,我得到一个IllegalStateException.

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

我所看到的所有帖子似乎都指向了一些复杂的方向,并提供了精心设计的解决方法.是不是有一些android处理方式?如果它是Dialog而不是DialogFragment,那么Activity的dismissDialog()会正确处理它.如果它是一个真正的DialogFragment而不是ACP中的一个,那么dismissAllowingStateLoss()会处理它.对于ACP版本的DialogFragment,是不是有这样的东西?

Ale*_*ood 22

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

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

  1. 活动执行AsyncTask.该AsyncTask节目一DialogFragmentonPreExecute(),并开始在后台线程执行其任务.
  2. 用户单击"主页"并Activity停止并强制进入后台.系统决定该设备的内存非常低,因此它决定它也应该销毁它Activity.
  3. AsyncTask完成和onPostExecute()被调用.在onPostExecute()你内部解雇DialogFragment使用commitAllowingStateLoss()以避免异常.
  4. 用户导航回到Activity.在FragmentManager将根据恢复其片段的状态Activity保存状态的.被保存的状态在onSaveInstanceState()被调用之后不知道任何事情,所以即使已经完成,DialogFragment也不会记住解雇该请求的请求并且DialogFragment将恢复AsyncTask.

由于偶尔会发生类似这些奇怪的错误,因此使用它commitAllowingStateLoss()来避免此异常通常不是一个好主意.因为AsyncTask回调方法(响应后台线程完成其工作Activity而调用)与生命周期方法完全无关(系统服务器进程响应系统范围的外部事件调用,例如设备掉落睡着了,或内存不足),处理这些情况需要你做一些额外的工作.当然,这些错误是非常罕见的,保护您的应用程序不受它们的影响通常不是1星评级和游戏商店的5星评级之间的差异......但它仍然需要注意.

希望这至少有一定意义.另外,请注意Dialogs也作为Activitys状态的一部分存在,所以尽管使用普通旧的Dialog可能会避免异常,但你基本上会遇到同样的问题(即,DialogActivity稍后恢复状态时,不会记住这个问题) .

坦率地说,最好的解决方案是避免在整个过程中显示对话AsyncTask.一个更加用户友好的解决方案是显示一个不确定的进度微调器ActionBar(例如G +和Gmail应用程序).导致用户界面发生重大变化以响应异步回调对用户体验不利,因为它是意外的并且突然使用户不再正在做他们正在做的事情.

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


Zac*_*sky 16

要解决非法状态异常问题并基本上实现dismissAllowingStateLoss()可以使用以下方法完成.

getFragmentManager().beginTransaction().remove(someDialog).commitAllowingStateLoss();
Run Code Online (Sandbox Code Playgroud)

这应该在没有hacky代码的情况下解决问题.如果线程使用dialog.show()通过处理程序与UI线程进行通信,则也可以应用于show.这也可能导致非法状态异常

getFragmentManager().beginTransaction().add(someDialog).commitAllowingStateLoss();
Run Code Online (Sandbox Code Playgroud)


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

getFragmentManager()
Run Code Online (Sandbox Code Playgroud)

getSupportFragmentManager()
Run Code Online (Sandbox Code Playgroud)


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

Activity应假定异步任务可能无法完成,并且它不会执行onPostExecute().无论UI动作(即,微调器,理想情况下不是对话框)开始通知用户异步操作,都应该有超时或跟踪状态自动停止并检查onRestore/onResume类型生命周期事件以确保UI已正确更新.服务也可能值得调查.