后台任务,进度对话框,方向更改 – 是否有任何100%的工作解决scheme?

我从后台线程(我使用AsyncTask )从互联网上下载一些数据,并下载时显示进度对话框。 方向更改,活动重新启动,然后我的AsyncTask完成 – 我想closuresprogess对话框,并开始一个新的活动。 但是调用dismissDialog有时会抛出一个exception(可能是因为Activity已经被销毁,新的Activity还没有被启动)。

什么是处理这种问题的最好方法(即使用户改变方向,从后台线程更新UI)? Google有人提供了一些“官方解决scheme”吗?

步骤1:使你的AsyncTask成为一个static嵌套类,或者一个完全独立的类,而不是一个内部(非静态嵌套)类。

第二步:让AsyncTask通过一个数据成员保存到Activity ,通过构造函数和setter来设置。

第3步:创buildAsyncTask ,将当前的Activity给构造函数。

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

第5步:在onCreate() ,如果getLastNonConfigurationInstance()不为null ,则将其转换为AsyncTask类,并调用setter将新活动与任务关联。

步骤#6:不要参考doInBackground()的活动数据成员。

如果你按照上面的配方,它会一切正常。 onProgressUpdate()onPostExecute()onPostExecute()的开始和后续onCreate()的结束之间挂起。

这是一个示范项目展示技术。

另一种方法是抛弃AsyncTask并将您的工作转移到IntentService 。 如果要完成的工作可能很长,并且应该继续进行,而不pipe用户在活动方面做了什么(例如,下载大文件),这是特别有用的。 您可以使用有序的广播Intent来让活动响应正在完成的工作(如果它仍然在前台)或者提出一个Notification让用户知道工作是否已经完成。 这里有一个关于这种模式更多的博客文章 。

接受的答案是非常有用的,但它没有进展对话框。

幸运的是,对于读者来说,我已经创build了一个非常全面的AsyncTask实例,并带有一个进度对话框 !

  1. 旋转工作,并对话生存。
  2. 您可以通过按下后退button来取消任务和对话框(如果需要这种行为)。
  3. 它使用碎片。
  4. 设备旋转时,活动下面的碎片布局会正确更改。

我已经辛苦了一个星期,find解决这个困境,而不诉诸编辑清单文件。 这个解决scheme的假设是:

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

履行

您将需要将这篇文章底部的两个文件复制到工作区中。 只要确保:

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

  2. onCreate() ,在初始化任何需要被ASyncTask访问的成员之后,应该调用super.onCreate() 。 此外,重写getContentViewId()以提供表单布局ID。

  3. 像平常一样重写onCreateDialog()来创build由活动pipe理的对话框。

  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 } } 

最后,启动你的新任务:

 mCurrentTask = new MyTask(this); ((MyTask) mCurrentTask).execute(); 

而已! 我希望这个强大的解决scheme将帮助某人。

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

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

有人从Google提供了一些“官方解决scheme”?

是。

解决scheme更多的是应用程序体系结构build议,而不仅仅是一些代码

他们提出了3种devise模式 ,允许应用程序与服务器同步工作,而不pipe应用程序状态如何(即使用户完成应用程序,用户更改屏幕,应用程序终止,每隔一个可能的状态一个后台数据操作可能会被中断,这就覆盖了它)

该build议在Virgil Dobjanschi的Google I / O 2010期间的Android REST客户端应用程序演讲中进行了解释。 这是1小时,但是非常值得一看。

其基础是将networking操作抽象为独立于应用程序中的任何ActivityService 。 如果您使用的是数据库,一旦使用获取的远程数据更新了本地数据库,使用ContentResolverCursor将为您提供一个开箱即用的Observer模式 ,便于更新UI,而无需任何辅助逻辑。 任何其他操作后的代码将通过传递给Service的callback(我使用ResultReceiver子类来运行)来运行。

无论如何,我的解释其实很含糊,你应该有把握地观看演讲。

虽然马克(CommonsWare)的答案确实适用于方向变化,但如果活动被直接销毁,就会失败(就像打电话一样)。

您可以通过使用Application对象来引用您的ASyncTask来处理方向更改和罕见的被破坏的Activity事件。

这里有一个很好的解决问题和解决scheme:

信贷完全由瑞安来找出这一个。

您应该使用活动处理程序调用所有活动操作。 所以如果你在某个线程中,你应该创build一个Runnable并使用Activitie的Handler进行发布。 否则,您的应用程序有时会崩溃致命的exception。

谷歌解决了4年后,在Activity onCreate中调用了setRetainInstance(true)的问题。 它将在设备旋转期间保留您的活动实例。 我也有一个较老的Android的简单解决scheme。

这是我的解决scheme: https : //github.com/Gotchamoh/Android-AsyncTask-ProgressDialog

基本上这些步骤是:

  1. 如果任务仍在处理,我使用onSaveInstanceState来保存任务。
  2. onCreate我得到的任务,如果它被保存。
  3. onPause ,如果显示ProgressDialog我会放弃它。
  4. onResume ,如果任务仍在处理,则显示ProgressDialog