首页 文章

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

提问于
浏览
230

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

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

8 回答

  • 0

    步骤#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有更多关于这种模式 .

  • 0

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

    幸运的是,读者,我创建了一个extremely comprehensive and working example of an AsyncTask with a progress dialog

    • 旋转工作,对话框仍然存在 .

    • 您可以通过按后退按钮取消任务和对话框(如果您需要此行为) .

    • 它使用片段 .

    • 当设备旋转时,活动下方片段的布局会正确更改 .

  • 1

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

    • 您始终需要使用进度对话框

    • 一次只执行一项任务

    • 当手机旋转并且进度对话框自动关闭时,您需要保留任务 .

    Implementation

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

    • 你的所有 Activity 应延长 BaseActivity

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

    • 覆盖 onCreateDialog() like usual以创建活动管理的对话框 .

    • 请参阅下面的代码,了解制作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();
    

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

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

    Yes.

    解决方案更多的是应用程序架构提案,而不仅仅是一些代码 .

    他们建议 3 design patterns 允许应用程序与服务器同步工作,无论应用程序状态如何(即使用户完成应用程序,用户更改屏幕,应用程序终止,每个其他可能的状态,后台都会工作)数据操作可能会被干扰,这涵盖了它)

    Virgil Dobjanschi在 Google I/O 2010 期间的演讲中解释了该提议 . 它是1小时长,但它是 extremely worth watching.

    它的基础是将网络操作抽象为 Service ,它独立于应用程序中的任何 Activity . 如果你正在与之合作数据库, ContentResolverCursor 的使用将为您提供一个开箱即用的 Observer pattern ,一旦您使用获取的远程数据更新本地数据库,便于更新UI而无需任何附加逻辑 . 任何其他的操作后代码都将通过传递给 Service 的回调运行(我为此使用 ResultReceiver 子类) .

    无论如何,我的解释实际上很模糊,你应该明确地观看演讲 .

  • 4

    虽然Mark的(CommonsWare)答案确实适用于方向更改,但如果活动被直接销毁(例如在电话呼叫的情况下),则会失败 .

    您可以使用Application对象引用ASyncTask来处理方向更改和罕见的已销毁Activity事件 .

    有一个很好的解释问题和解决方案here

    信用证完全归功于瑞安,因为他把这个问题搞清楚了 .

  • 13

    4年后,Google解决了在Activity onCreate中调用setRetainInstance(true)的问题 . 它将在设备轮换期间保留您的活动实例 . 对于较旧的Android,我也有一个简单的解决方案 .

  • 2

    您应该使用活动处理程序调用所有活动操作 . 所以如果你在某个线程中,你应该创建一个Runnable并使用Activitie的Handler发布 . 否则,您的应用程序有时会因致命异常而崩溃 .

  • 8

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

    基本上步骤是:

    • 如果任务仍处理,我使用 onSaveInstanceState 保存任务 .

    • onCreate 我得到了保存的任务 .

    • onPause 中,如果显示 ProgressDialog ,则将其丢弃 .

    • onResume 中如果任务仍在处理,则显示 ProgressDialog .

相关问题