我在后台线程中从Internet下载了一些数据(我使用AsyncTask
),并在下载时显示了进度对话框。方向更改,Activity重新启动,然后我的AsyncTask完成-我想关闭进度对话框并启动新的Activity。但是,调用dismissDialog有时会引发异常(可能是因为Activity已被破坏并且尚未启动新的Activity)。
解决此类问题的最佳方法是什么(即使用户更改了方向,也从可以工作的后台线程更新UI)?Google有人提供了一些“官方解决方案”吗?
我在后台线程中从Internet下载了一些数据(我使用AsyncTask
),并在下载时显示了进度对话框。方向更改,Activity重新启动,然后我的AsyncTask完成-我想关闭进度对话框并启动新的Activity。但是,调用dismissDialog有时会引发异常(可能是因为Activity已被破坏并且尚未启动新的Activity)。
解决此类问题的最佳方法是什么(即使用户更改了方向,也从可以工作的后台线程更新UI)?Google有人提供了一些“官方解决方案”吗?
Answers:
步骤#1:让您AsyncTask
的static
嵌套类或完全独立的类,而不是内部(非静态嵌套)类。
步骤#2:具有AsyncTask
保持到Activity
通过数据构件,通过构造和一个setter设置。
步骤#3:创建时AsyncTask
,将电流提供Activity
给构造函数。
步骤#4:在中onRetainNonConfigurationInstance()
,AsyncTask
从中分离出原始活动之后返回。
步骤#5:在中onCreate()
(如果getLastNonConfigurationInstance()
不是)null
,将其AsyncTask
强制转换为您的班级,然后调用setter将新活动与任务相关联。
步骤#6:不要引用中的活动数据成员doInBackground()
。
如果您遵循上述食谱,则可以正常使用。onProgressUpdate()
并在后续onPostExecute()
的开始onRetainNonConfigurationInstance()
和结束之间暂停onCreate()
。
另一种方法是抛弃AsyncTask
并将您的工作移至IntentService
。如果要完成的工作可能很长,并且无论用户在活动方面做了什么(例如,下载大文件),都应该继续进行,那么这特别有用。您可以使用有序广播Intent
来使活动对正在完成的工作做出响应(如果活动仍在前台),或者提出一个Notification
以通知用户是否已完成工作。这是一篇有关此模式的博客文章。
onRetainNonConfigurationInstance()
不推荐使用,建议的替代方法是使用setRetainInstance()
,但它不返回任何对象。是否可以使用来处理asyncTask
配置更改setRetainInstance()
?
Fragment
保留的AsyncTask
。有Fragment
来电setRetainInstance(true)
时本身。与AsyncTask
唯一的谈话Fragment
。现在,在配置更改中,Fragment
不会销毁并重新创建(即使活动处于活动状态),因此在AsyncTask
整个配置更改中都会保留。
接受的答案非常有帮助,但是没有进度对话框。
读者,幸运的是,我为您创建了一个非常全面且可运行的AsyncTask示例,其中带有进度对话框!
我花了一个星期的时间来寻找解决这个难题的方法,而不必借助清单文件。该解决方案的假设是:
实作
您将需要将本博文底部找到的两个文件复制到您的工作区中。只需确保:
你所有Activity
的应该扩展BaseActivity
在中onCreate()
,super.onCreate()
应在初始化需要由ASyncTask
s 访问的任何成员之后调用。另外,重写getContentViewId()
以提供表单布局ID。
onCreateDialog()
像往常一样重写以创建由活动管理的对话框。
请参阅下面的代码,获取用于创建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();
而已!我希望这个强大的解决方案能对某人有所帮助。
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有人提供了一些“官方解决方案”吗?
是。
解决方案更多是一个应用程序体系结构建议,而不是一些代码。
他们提出了3种设计模式,无论应用程序处于什么状态,应用程序都可以与服务器同步工作(即使用户完成应用程序,用户更改屏幕,终止应用程序,其他可能的状态,应用程序也可以正常工作)可能会中断后台数据操作,这涵盖了它)
Virgil Dobjanschi 在Google I / O 2010期间的Android REST客户端应用程序演讲中解释了该建议。它是1小时长,但是非常值得一看。
它的基础是将网络操作抽象Service
为Activity
与应用程序中的任何一个独立工作的。如果您使用的是数据库,则当您使用所获取的远程数据更新本地数据库时,使用ContentResolver
和Cursor
将为您提供开箱即用的Observer模式,该模式可方便地更新UI而无需任何附加逻辑。其他任何后续操作代码都将通过传递给的回调Service
(我ResultReceiver
为此使用子类)来运行。
无论如何,我的解释实际上很模糊,您应该明确地观看演讲。
四年后,Google解决了该问题,仅在Activity onCreate中调用setRetainInstance(true)。它将在设备轮换期间保留您的活动实例。对于较旧的Android,我也有一个简单的解决方案。
这是我的解决方案:https : //github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
基本上,这些步骤是:
onSaveInstanceState
如果任务仍在处理中,我会使用它来保存。onCreate
我得到的任务,如果它被保存。onPause
我丢弃ProgressDialog
如果显示。onResume
,显示ProgressDialog
任务是否仍在处理中。