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

タスクをアクティビティのライフサイクルと同期させる必要がある場合は、ローダーがまさに必要なものであると思います。具体的には、AsyncTaskLoaderを使用してジョブを実行する必要があります。したがって、AsyncTaskを実行する代わりに、ローダーを起動して、リスナーで応答を待ちます。アクティビティが一時停止されている場合、コールバックは取得されません。この部分は自動的に管理されます。

このタスクを処理する別の方法があります。インスタンスを保持するフラグメントを使用することです。一般的な考え方は、UIを使用せずにフラグメントを作成し、を呼び出すことsetRetainInstance(true)です。アクティビティが利用可能かどうかについて通知されるタスクがあります。そうでない場合、タスクのスレッドはアクティビティが利用可能になるまで一時停止します。

于 2011-11-03T09:28:50.483 に答える
8

必要なことを実現する別の方法は、この投稿で文書化したPauseHandlerクラスを実装することです。

次に、onPostExecuteメソッドでsendMessage()を呼び出して、メッセージをハンドラーに投稿します。

アプリケーションが再開すると、アクションが処理されます。

于 2011-11-14T12:49:23.660 に答える
3

BroadcastReceiverを使用するよりも、guava、otto、eventbusなどのバスライブラリを使用する方が好きです。それらのパフォーマンスは、放送受信機の実装よりもはるかに優れています。

于 2016-01-21T14:18:28.593 に答える
2

大きな回避策なしでこの問題の解決策を思いつきました。progressdialogとasynctaskを維持する方法の基本的な考え方は、このブログエントリに記載されています(もちろん、 AsyncTaskComplex -Versionを使用しました)。すべてのクレジットはこのブログエントリの作成者に送られます。私はほんの小さなことを追加しました。

明らかに、私はもうshowDialog()を使用していません。代わりに、DialogFragmentsを使い続けます。

2番目の調整は重要な調整であり、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

アクティビティ/フラグメントが一時停止しているときにハンドラメッセージを処理する方法について、BroadcastReceiverを使用した別のアプローチを提供します。

よりクリーンでエレガントだと思います。アプリ内のどこからでもベースフラグメントのコードを呼び出すことができ、スティッキーブロードキャストを使用することで、フラグメントの再開後に呼び出しを「記憶」して実行できるという利点があります。

于 2014-09-28T10:28:55.363 に答える