如何在暂停时处理AsyncTask onPostExecute以避免IllegalStateException

时间:2011-11-03 09:19:11

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

我很欣赏有关轮换更改的AsyncTask的大量帖子。使用兼容性lib并尝试解除DialogFragment中的onPostExecute时出现以下问题。

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

如果正在显示进度对话框时我将应用程序放到后台我得到了以下片段:

1)onPause

2)onSaveInstanceState

3)onPostExecute我尝试解雇并调用对话框。

我得到一个IllegalStateException,因为我正在尝试在活动保存状态时有效地提交事务,我理解这一点。

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

我的问题是,我的解决方案只是在onPostExecute中检测到片段/活动是暂停的,只是在onResume执行我需要做的事情吗?对我来说似乎有些难看。

先谢谢,彼得。

修改1

需要支持2.1及以上

修改2

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

5 个答案:

答案 0 :(得分:15)

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

还有另一种方法可以处理此任务:使用retains its instance的片段。一般的想法是你创建一个没有UI的片段并调用setRetainInstance(true)。它有一个任务,正在通知可用或不可用的活动。如果没有,任务的线程将暂停,直到活动可用。

答案 1 :(得分:8)

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

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

当您的申请恢复时,将处理该行动。

答案 2 :(得分:3)

而不是使用BroadcastReceiver,我更喜欢使用像guava,otto或eventbus这样的总线库。它们的性能比广播接收器实现要好得多。

答案 3 :(得分:2)

我想出了一个解决这个问题的方法,没有任何重大的解决方法: 这个blogentry中描述了如何维护progressdialog和asynctask的基本思想(当然我使用的是AsyncTaskComplex-Version)。所有的归功于这个流行的作者,我只添加了一个小东西:

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

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

不仅仅告诉onRetainCustomNonConfigurationInstance()中的asynctask没有更多的活动,我也在onPause()中执行它。而不只是告诉onCreate()中的asynctask有一个新的活动我也在onResume()中做。

然后你去了,当活动不可见时,你的AsyncTask不会尝试通知你的活动他的完成导致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();
    }
}

}

答案 4 :(得分:2)

How to handle Handler messages when activity/fragment is paused上我使用BroadcastReceiver提供了另一种方法。

我认为它更清洁更优雅,它提供的优势是,您可以从应用程序中的任何位置调用基本片段上的代码,并通过使用粘性广播,您的调用可以被记住&#34;并在片段恢复后执行。