另一种方法是放弃 AsyncTask 并将你的工作转移到 IntentService . 如果要完成的工作可能很长并且应该继续进行而不管用户在活动方面做了什么(例如,下载大文件),则这尤其有用 . 您可以使用有序广播 Intent 让活动响应正在进行的工作(如果它仍然在前台)或者引发 Notification 以让用户知道工作是否已完成 . Here is a blog post有更多关于这种模式 .
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();
That's it! 我希望这个强大的解决方案能够帮到某些人 .
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;
}
}
8 回答
步骤#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()
结束之间暂停 .Here is a sample project展示了这项技术 .
另一种方法是放弃
AsyncTask
并将你的工作转移到IntentService
. 如果要完成的工作可能很长并且应该继续进行而不管用户在活动方面做了什么(例如,下载大文件),则这尤其有用 . 您可以使用有序广播Intent
让活动响应正在进行的工作(如果它仍然在前台)或者引发Notification
以让用户知道工作是否已完成 . Here is a blog post有更多关于这种模式 .接受的答案非常有用,但它没有进度对话框 .
幸运的是,读者,我创建了一个extremely comprehensive and working example of an AsyncTask with a progress dialog!
旋转工作,对话框仍然存在 .
您可以通过按后退按钮取消任务和对话框(如果您需要此行为) .
它使用片段 .
当设备旋转时,活动下方片段的布局会正确更改 .
我花了一个星期的时间来寻找解决这个难题的方法而不需要编辑清单文件 . 该解决方案的假设是:
您始终需要使用进度对话框
一次只执行一项任务
当手机旋转并且进度对话框自动关闭时,您需要保留任务 .
Implementation
您需要将此帖子底部的两个文件复制到工作区中 . 请确保:
你的所有
Activity
应延长BaseActivity
在
onCreate()
中,应在初始化ASyncTask
需要访问的任何成员后调用super.onCreate()
. 另外,覆盖getContentViewId()
以提供表单布局ID .覆盖
onCreateDialog()
like usual以创建活动管理的对话框 .请参阅下面的代码,了解制作AsyncTasks的示例静态内部类 . 您可以将结果存储在mResult中以便以后访问 .
最后,要启动新任务:
That's it! 我希望这个强大的解决方案能够帮到某些人 .
BaseActivity.java (自己组织进口)
SuperAsyncTask.java
Yes.
解决方案更多的是应用程序架构提案,而不仅仅是一些代码 .
他们建议 3 design patterns 允许应用程序与服务器同步工作,无论应用程序状态如何(即使用户完成应用程序,用户更改屏幕,应用程序终止,每个其他可能的状态,后台都会工作)数据操作可能会被干扰,这涵盖了它)
Virgil Dobjanschi在 Google I/O 2010 期间的演讲中解释了该提议 . 它是1小时长,但它是 extremely worth watching.
它的基础是将网络操作抽象为
Service
,它独立于应用程序中的任何Activity
. 如果你正在与之合作数据库,ContentResolver
和Cursor
的使用将为您提供一个开箱即用的 Observer pattern ,一旦您使用获取的远程数据更新本地数据库,便于更新UI而无需任何附加逻辑 . 任何其他的操作后代码都将通过传递给Service
的回调运行(我为此使用ResultReceiver
子类) .无论如何,我的解释实际上很模糊,你应该明确地观看演讲 .
虽然Mark的(CommonsWare)答案确实适用于方向更改,但如果活动被直接销毁(例如在电话呼叫的情况下),则会失败 .
您可以使用Application对象引用ASyncTask来处理方向更改和罕见的已销毁Activity事件 .
有一个很好的解释问题和解决方案here:
信用证完全归功于瑞安,因为他把这个问题搞清楚了 .
4年后,Google解决了在Activity onCreate中调用setRetainInstance(true)的问题 . 它将在设备轮换期间保留您的活动实例 . 对于较旧的Android,我也有一个简单的解决方案 .
您应该使用活动处理程序调用所有活动操作 . 所以如果你在某个线程中,你应该创建一个Runnable并使用Activitie的Handler发布 . 否则,您的应用程序有时会因致命异常而崩溃 .
这是我的解决方案:https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
基本上步骤是:
如果任务仍处理,我使用
onSaveInstanceState
保存任务 .在
onCreate
我得到了保存的任务 .在
onPause
中,如果显示ProgressDialog
,则将其丢弃 .在
onResume
中如果任务仍在处理,则显示ProgressDialog
.