我下载了一些数据从互联网在后台线程(我使用AsyncTask)和显示进度对话框,而下载。方向改变,活动重新启动,然后我的AsyncTask完成-我想解散进程对话框,并启动一个新的活动。但是调用disdisdialog有时会抛出一个异常(可能是因为Activity已经被销毁并且新的Activity还没有启动)。
处理这类问题的最佳方法是什么(从后台线程更新UI,即使用户改变方向也能工作)?谷歌是否有人提供了一些“官方解决方案”?
我下载了一些数据从互联网在后台线程(我使用AsyncTask)和显示进度对话框,而下载。方向改变,活动重新启动,然后我的AsyncTask完成-我想解散进程对话框,并启动一个新的活动。但是调用disdisdialog有时会抛出一个异常(可能是因为Activity已经被销毁并且新的Activity还没有启动)。
处理这类问题的最佳方法是什么(从后台线程更新UI,即使用户改变方向也能工作)?谷歌是否有人提供了一些“官方解决方案”?
当前回答
为了找到解决这个困境的方法,我已经辛苦了一个星期,而不是求助于编辑清单文件。这个解决方案的假设是:
您总是需要使用进度对话框 一次只执行一个任务 您需要在电话旋转时保持任务,并且进度对话框将自动退出。
实现
你需要将这篇文章底部的两个文件复制到你的工作区中。只要确保:
你所有的activity都应该扩展BaseActivity 在onCreate()中,super.onCreate()应该在初始化ASyncTasks需要访问的任何成员后调用。同样,重写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();
就是这样!我希望这个强大的解决方案能帮助到一些人。
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;
}
}
其他回答
谷歌是否有人提供了一些“官方解决方案”?
Yes.
解决方案更像是一个应用程序体系结构建议,而不仅仅是一些代码。
他们提出了3种设计模式,允许应用程序与服务器同步工作,无论应用程序状态如何(即使用户完成应用程序,用户更换屏幕,应用程序被终止,任何其他可能的状态,后台数据操作可能被中断,它也会工作,这包括在内)
Virgil Dobjanschi在谷歌I/O 2010期间的Android REST客户端应用程序演讲中解释了这个提议。它只有1小时长,但非常值得一看。
它的基础是将网络操作抽象为独立于应用程序中的任何活动的服务。如果您正在使用数据库,使用ContentResolver和Cursor将为您提供一个开箱即用的Observer模式,一旦您使用获取的远程数据更新了本地数据库,就可以方便地更新UI,而无需任何额外的逻辑。任何其他后续操作代码都将通过传递给服务的回调运行(为此我使用ResultReceiver子类)。
不管怎样,我的解释其实很模糊,你一定要看这个演讲。
这是我的解决方案:https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
基本步骤如下:
我使用onSaveInstanceState保存任务,如果它仍然 处理。 在onCreate我得到的任务,如果它被保存。 在onPause我放弃ProgressDialog如果它是显示。 在onResume中,如果任务仍然,我将显示ProgressDialog 处理。
为了找到解决这个困境的方法,我已经辛苦了一个星期,而不是求助于编辑清单文件。这个解决方案的假设是:
您总是需要使用进度对话框 一次只执行一个任务 您需要在电话旋转时保持任务,并且进度对话框将自动退出。
实现
你需要将这篇文章底部的两个文件复制到你的工作区中。只要确保:
你所有的activity都应该扩展BaseActivity 在onCreate()中,super.onCreate()应该在初始化ASyncTasks需要访问的任何成员后调用。同样,重写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();
就是这样!我希望这个强大的解决方案能帮助到一些人。
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;
}
}
您应该使用活动处理程序调用所有活动操作。因此,如果你在某个线程中,你应该创建一个Runnable,并使用活动的处理程序发布。否则你的应用程序有时会崩溃,出现致命异常。
4年后,谷歌解决了问题,只是在onCreate活动中调用setRetainInstance(true)。它将在设备旋转期间保存您的活动实例。对于旧版本的Android,我也有一个简单的解决方案。