后台任务,进度对话框,方向更改 - 是否有100%可行的解决方案?

fhu*_*cho 233 android orientation android-orientation

我在后台线程(我使用AsyncTask)下载了一些来自互联网的数据,并在下载时显示进度对话框.方向更改,Activity重新启动,然后我的AsyncTask完成 - 我想关闭progess对话框并启动一个新的Activity.但是调用dismissDialog有时会抛出一个异常(可能是因为Activity被销毁而且还没有启动新的Activity).

处理此类问题的最佳方法是什么(从后台线程更新UI即使用户更改方向也能正常工作)?谷歌有人提供了一些"官方解决方案"吗?

Com*_*are 335

步骤#1:使您AsyncTaskstatic嵌套类或完全独立的类,而不是内部(非静态嵌套)类.

步骤#2:AsyncTask保持Activity通过数据成员,通过构造函数和setter设置.

步骤3:创建时AsyncTask,将电流提供Activity给构造函数.

步骤#4:在将其从原始的,即将开始的活动中分离之后onRetainNonConfigurationInstance(),返回AsyncTask.

步骤5:onCreate()如果getLastNonConfigurationInstance()不是null,请将其投射到您的AsyncTask班级并致电您的设置者将您的新活动与任务相关联.

步骤#6:不要从中引用活动数据成员doInBackground().

如果您按照上面的配方,它将全部工作.onProgressUpdate()并在后续onPostExecute()的开始onRetainNonConfigurationInstance()和结束之间暂停onCreate().

这是一个展示该技术的示例项目.

另一种方法是放弃AsyncTask并将你的工作转移到IntentService.如果要完成的工作可能很长并且应该继续进行而不管用户在活动方面做了什么(例如,下载大文件),这尤其有用.您可以使用有序广播Intent让活动响应正在进行的工作(如果它仍然在前台)或者引发Notification以让用户知道工作是否已完成.这是一篇关于这种模式的博文.

  • 不推荐使用`onRetainNonConfigurationInstance()`,建议的替代方法是使用`setRetainInstance()`,但它不返回对象.是否可以使用`setRetainInstance()`处理配置更改时的`asyncTask`? (11认同)
  • @SYLARRR:绝对.让`Fragment`拥有`AsyncTask`.让`Fragment`自己调用`setRetainInstance(true)`.让'AsyncTask'只与'Fragment`对话.现在,在配置更改时,`Fragment`不会被销毁和重新创建(即使活动是),因此在配置更改中保留`AsyncTask`. (10认同)
  • 非常感谢您对这个常见问题的出色答案!为了彻底,您可以在步骤#4中添加我们必须在AsyncTask中分离(设置为null)活动.但是,在示例项目中很好地说明了这一点. (8认同)
  • 但是如果我需要访问Activity的成员呢? (3认同)
  • @Andrew:创建一个静态内部类或者保存在几个对象上的东西,然后返回它. (3认同)
  • @numan不,"AsyncTaskLoader"不是推荐的做法,因为它根本不是一回事."AsyncTask"执行单个异步操作."AsyncTaskLoader"为"Activity"和/或"Fragment"执行异步加载,在配置更改时保留加载的数据,并在检测到数据源更改时自动执行新加载.如果你需要执行单次,一次性,可能很昂贵的操作,那么使用`AsyncTaskLoader`是没有意义的......而是使用`AsyncTask`. (3认同)

Tim*_*mmm 13

接受的答案非常有用,但它没有进度对话框.

幸运的是,对于您,读者,我已经创建了一个非常全面且有效的AsyncTask示例和一个进度对话框!

  1. 旋转工作,对话存活.
  2. 您可以通过按后退按钮取消任务和对话框(如果您需要此行为).
  3. 它使用片段.
  4. 当设备旋转时,活动下方片段的布局会正确更改.


Ole*_*ich 9

我花了一个星期的时间来寻找解决这个难题的方法而不需要编辑清单文件.该解决方案的假设是:

  1. 您始终需要使用进度对话框
  2. 一次只执行一项任务
  3. 您需要在旋转手机时保持任务,并且进度对话框将自动解除.

履行

您需要将此帖子底部的两个文件复制到工作区中.请确保:

  1. Activity的所有s都应该扩展BaseActivity

  2. 在初始化您需要访问的任何成员后onCreate(),super.onCreate()应该调用ASyncTask.另外,覆盖getContentViewId()以提供表单布局ID.

  3. onCreateDialog() 像往常一样覆盖以创建由活动管理的对话框.

  4. 请参阅下面的代码,了解制作AsyncTasks的示例静态内部类.您可以将结果存储在mResult中以便以后访问.


final static class MyTask extends SuperAsyncTask<Void, Void, Void> {

    public OpenDatabaseTask(BaseActivity activity) {
        super(activity, MY_DIALOG_ID); // change your dialog ID here...
                                       // and your dialog will be managed automatically!
    }

    @Override
    protected Void doInBackground(Void... params) {

        // your task code

        return null;
    }

    @Override
    public boolean onAfterExecute() {
        // your after execute code
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,要启动新任务:

mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();
Run Code Online (Sandbox Code Playgroud)

而已!我希望这个强大的解决方案可以帮助某人

BaseActivity.java(自己组织导入)

protected abstract int getContentViewId();

public abstract class BaseActivity extends Activity {
    protected SuperAsyncTask<?, ?, ?> mCurrentTask;
    public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(getContentViewId());

        mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
        if (mCurrentTask != null) {
            mCurrentTask.attach(this);
            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
        mCurrentTask.postExecution();
            }
        }
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
    super.onPrepareDialog(id, dialog);

        mDialogMap.put(id, true);
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (mCurrentTask != null) {
            mCurrentTask.detach();

            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
                return mCurrentTask;
            }
        }

        return super.onRetainNonConfigurationInstance();
    }

    public void cleanupTask() {
        if (mCurrentTask != null) {
            mCurrentTask = null;
            System.gc();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

SuperAsyncTask.java

public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
    protected BaseActivity mActivity = null;
    protected Result mResult;
    public int dialogId = -1;

    protected abstract void onAfterExecute();

    public SuperAsyncTask(BaseActivity activity, int dialogId) {
        super();
        this.dialogId = dialogId;
        attach(activity);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mActivity.showDialog(dialogId); // go polymorphism!
    }    

    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        mResult = result;

        if (mActivity != null &&
                mActivity.mDialogMap.get((Integer) dialogId) != null
                && mActivity.mDialogMap.get((Integer) dialogId)) {
            postExecution();
        }
    };

    public void attach(BaseActivity activity) {
        this.mActivity = activity;
    }

    public void detach() {
        this.mActivity = null;
    }

    public synchronized boolean postExecution() {
        Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
        if (dialogExists != null || dialogExists) {
            onAfterExecute();
            cleanUp();
    }

    public boolean cleanUp() {
        mActivity.removeDialog(dialogId);
        mActivity.mDialogMap.remove((Integer) dialogId);
        mActivity.cleanupTask();
        detach();
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*sco 5

谷歌有人提供了一些“官方解决方案”吗?

是的。

该解决方案更多的是一个应用程序架构建议,而不仅仅是一些代码

他们提出了3 种设计模式,允许应用程序与服务器同步工作,而不管应用程序状态如何(即使用户完成应用程序、用户更改屏幕、应用程序被终止以及所有其他可能的状态,它也会工作)后台数据操作可能会被中断,这涵盖了它)

Virgil Doobjanschi在Google I/O 2010期间的Android REST 客户端应用程序演讲中解释了该提案。虽然片长1个小时,但是非常值得一看。

它的基础是将网络操作抽象为Service独立于Activity应用程序中的任何操作的操作。如果您正在使用数据库,那么使用ContentResolverCursor将为您提供一种开箱即用的观察者模式,一旦您使用获取的远程数据更新本地数据库,就可以方便地更新 UI,而无需任何其他逻辑。任何其他操作后代码都将通过传递给的回调运行Service(我ResultReceiver为此使用子类)。

无论如何,我的解释实际上很模糊,你一定要看看演讲。