41

I appreciate the numerous postings regarding AsyncTask on a rotation change. I have the following problem when using the compatability lib and trying to dismiss a DialogFragment in onPostExecute.

I have a fragment which fires of an AsyncTask which displays a progress DialogFragment, then in onPostExecute dismisses the dialog and then potentially throws up another DialogFragment.

If when the progress dialog is being displayed I put the application into the background I get the following for my fragment:

1) onPause

2) onSaveInstanceState

3) onPostExecute in which I try to dismiss and invoke a dialog.

I get an IllegalStateException because I'm trying to effectively commit a transaction when the activity has saved its state and I understand this.

On a rotation I've assumed (perhaps incorrectly) that I wouldn't get an onPostExecute until the activity has been recreated. However, when putting the application into the background I assumed (definitely incorrectly) that the onPostExectute wouldn't get called while the fragment/activity was paused.

My question is, is my solution to simply detect in onPostExecute that the fragment/activity is paused and simply perform what I need to do in onResume instead? Seems somewhat ugly to me.

Thanks in advance, peter.

Edit 1

Need to support 2.1 and above

Edit 2

I have considered showing the dialog using FragmentTransaction:add and FragmentTransaction:commitAllowingStateLosshowever this isn't without its problems.

4

5 回答 5

16

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

还有另一种处理此任务的方法:使用保留其实例的片段。一般的想法是您创建一个没有 UI 的片段并调用setRetainInstance(true). 它有一个任务被通知活动是否可用。如果没有,任务的线程将挂起,直到活动变得可用。

于 2011-11-03T09:28:50.483 回答
8

实现所需的另一种方法是实现我在这篇文章中记录的 PauseHandler 类。

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

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

于 2011-11-14T12:49:23.660 回答
3

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

于 2016-01-21T14:18:28.593 回答
2

我想出了一个解决这个问题的方法,没有任何主要的解决方法:这个博客条目中描述了如何维护进度对话框和异步任务的基本思想(当然我使用了 AsyncTaskComplex-Version)所有学分归作者所有,我只添加了一点:

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

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

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

你去那里了,当活动不可见时,你的 AsyncTask 不会尝试通知你的活动他的完成导致 IllegalStateException 。

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

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

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();
    }
}

}

于 2011-11-08T11:59:11.760 回答
2

关于如何在活动/片段暂停时处理处理程序消息,我提供了另一种使用广播接收器的方法。

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

于 2014-09-28T10:28:55.363 回答