【问题标题】:How to handle AsyncTask onPostExecute when paused to avoid IllegalStateException暂停时如何处理 AsyncTask onPostExecute 以避免 IllegalStateException
【发布时间】:2011-11-03 09:19:10
【问题描述】:

感谢关于轮换更改的 AsyncTask 的大量帖子。使用兼容性库并尝试在onPostExecute 中关闭DialogFragment 时遇到以下问题。

我有一个触发 AsyncTask 的片段,它显示进度 DialogFragment,然后在 onPostExecute 中关闭对话框,然后可能抛出另一个 DialogFragment

如果在显示进度对话框时我将应用程序置于后台,我的片段会得到以下信息:

1)onPause

2)onSaveInstanceState

3) onPostExecute 我尝试关闭并调用一个对话框。

我收到IllegalStateException 是因为我正在尝试在活动保存其状态并且我理解这一点时有效地提交事务。

在轮换中,我假设(可能是错误的)在重新创建活动之前我不会得到onPostExecute。但是,当将应用程序置于后台时,我假设(绝对错误地)onPostExectute 在片段/活动暂停时不会被调用。

我的问题是,我的解决方案是简单地在onPostExecute 中检测到片段/活动已暂停,并在onResume 中简单地执行我需要做的事情吗?我觉得有点丑。

提前致谢,彼得。

编辑 1

需要支持2.1及以上

编辑 2

我考虑过使用FragmentTransaction:addFragmentTransaction:commitAllowingStateLoss 显示对话框,但这并非没有问题。

【问题讨论】:

  • 我的blog post 关于片段事务和活动状态丢失可能会有所帮助。 :)

标签: android android-asynctask android-fragments android-lifecycle


【解决方案1】:

如果您需要将您的任务与活动生命周期同步,我相信Loaders 正是您所需要的。更具体地说,您应该使用AsyncTaskLoader 来完成这项工作。所以现在不是运行 AsyncTask,而是启动加载器,然后等待侦听器中的响应。如果activity被暂停,你不会得到回调,这部分会为你管理。

还有另一种方法来处理这个任务:使用retains its instance 的片段。一般的想法是你创建一个没有 UI 的片段并调用setRetainInstance(true)。它有一个任务被通知活动是否可用。如果没有,任务的线程会挂起,直到有活动可用。

【讨论】:

  • 谢谢,需要进一步调查,尽管在我的情况下感觉有点像大锤来破解坚果,在暂停时延迟 UI 任务将是简单的解决方案。
  • 我现在正在解决一些相同的问题,我找到了一个新的解决方案。也许它也会帮助你。看看吧,我已经更新了我的答案。
  • @Malcolm 你能提供任何文件的链接,上面写着If the activity is paused, you won't get a callback吗?我在 loaders 文档中的任何地方都找不到它。
  • @zack 据我所知,文档中没有明确说明,但我查看了源代码,这就是它的行为方式。不过,我仍然会检查应用程序是否已恢复。
【解决方案2】:

实现您所需的另一种方法是实现我记录的 PauseHandler 类 in this post

然后在您的 onPostExecute 方法中调用 sendMessage() 将您的消息发布到处理程序中。

当您的应用程序恢复时,将处理该操作。

【讨论】:

    【解决方案3】:

    比起使用 BroadcastReceiver,我更喜欢使用 guava、otto 或 eventbus 等总线库。它们的性能比广播接收器实现要好得多。

    【讨论】:

      【解决方案4】:

      我想出了一个解决这个问题的方法,但没有任何主要的解决方法: 这个blogentry 描述了如何维护progressdialog 和asynctask 的基本思想(当然我使用了AsyncTaskComplex-Version)。所有学分归作者所有,我只添加了一点:

      显然我不再使用 showDialog() 了。相反,我坚持使用 DialogFragments。

      第二个调整很重要,也解决了 IllegalStateException 的问题:

      我不仅在 onRetainCustomNonConfigurationInstance() 中告诉异步任务没有更多活动,还在 onPause() 中执行此操作。而不是只告诉 onCreate() 中的 asynctask 有一个新活动,我还在 onResume() 中执行它。

      然后,当 Activity 不可见时,您的 AsyncTask 不会尝试通知您的 Activity 他的完成导致 IllegalStateException。

      如果您想查看更多代码而不是文字,请发表评论。

      /编辑: 显示我的解决方案的源代码,我认为这是一个相当不错的解决方案:)

      public class MyActivity extends Activity {
      
      private MyTask mTask;
      
      @Override
      protected void onCreate(Bundle pSavedInstanceState) {
          super.onCreate(pSavedInstanceState);
          setContentView(R.layout.editaccount);
      
          Object retained = getLastCustomNonConfigurationInstance();
          if ( retained instanceof NewContactFolderIdTask ) {
              mTask = (MyTask) retained;
              mTask.setActivity(this);
          }
      
      }
      @Override
      protected void onPause() {
          if(mTask != null) {
              mTask.setActivity(null);
          }
          super.onPause();
      }
      
      @Override
      public Object onRetainCustomNonConfigurationInstance() {
          if(mTask != null) {
              mTask.setActivity(null);
              return mTask;
          }
          return null;
      }
      
      @Override
      protected void onResume() {
          if(mTask != null) {
              mTask.setActivity(this);
          }
          loadValues(); // or refreshListView or whatever you need to do
          super.onResume();
      }
      
      public void onTaskCompleted() {
          loadValues();  // or refreshListView or whatever you need to do
          DialogFragment dialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(PROGRESS_DIALOG_FRAGMENT);
          if(dialogFragment != null) {
              dialogFragment.dismiss();
          }
      }
      
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          MenuInflater menuInflater = getMenuInflater();
          menuInflater.inflate(R.menu.main, menu);
          return super.onCreateOptionsMenu(menu);
      }
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          switch (item.getItemId()) {
              case android.R.id.home:
                  // app icon in Action Bar clicked; go home
                  Intent intent = new Intent(this, OXClient.class);
                  intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                  startActivity(intent);
                  return true;
              case R.id.menu_refresh:
                  mTask = new MyTask(this);
                  mTask.execute();
                  break;
          }
          return super.onOptionsItemSelected(item);
      }
      
      
      private class NewContactFolderIdTask extends AsyncTask<Boolean, Integer, Bundle> {
          private MyActivity mActivity;
          private boolean mCompleted;
      
          private NewContactFolderIdTask(MyActivity pActivity) {
              this.mActivity = pActivity;
          }
      
          public void setActivity(MyActivity pActivity) {
              this.mActivity = pActivity;
              if(mCompleted) {
                  notifiyActivityTaskCompleted();
              }
          }
      
          private void notifiyActivityTaskCompleted() {
              if(mActivity != null) {
                  mActivity.onTaskCompleted();
              }
          }
      
          @Override
          protected Bundle doInBackground(Boolean... pBoolean) {
              // Do your stuff, return result
          }
      
          @Override
          protected void onPreExecute() {
              DialogFragment newFragment = ProgressDialogFragment.newInstance();
              newFragment.show(getSupportFragmentManager(), PROGRESS_DIALOG_FRAGMENT);
          }
      
          @Override
          protected void onPostExecute(Bundle pResult) {
              mCompleted = true;
              notifiyActivityTaskCompleted();
          }
      }
      

      }

      【讨论】:

      • 谢谢,以上内容很好,但是如果您在获得 onPostExecute 之前将应用程序置于后台,我仍然希望 dialogFragment 被关闭,此时您会看到非法状态异常,除非我是遗漏了一些明显的东西。
      • 是的,你是对的。当我的 asyncTask 在活动/应用程序处于后台时完成时,我也会收到 IllegalStateException。以前从未尝试过:/
      • 很抱歉没有仔细阅读您的问题,完全超出了“放在后台”部分:/
      • 我解决了这个问题,至少我的本地测试都是阳性的。正如我已经说过的:如果您想查看更多代码,请发表评论。
      • 一定有兴趣看看你做了什么。目前我正在考虑 AsyncTaskLoader 或只是将onPostExecute 功能延迟到onResume
      【解决方案5】:

      How to handle Handler messages when activity/fragment is paused 上,我提供了另一种使用广播接收器的方法。

      我认为它更简洁更优雅,它提供的优势是您可以从应用内的任何位置调用基础片段上的代码,并且通过使用粘性广播,您的调用可以在片段恢复后被“记住”并执行。

      【讨论】:

        猜你喜欢
        • 2018-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-28
        • 1970-01-01
        相关资源
        最近更新 更多