web-dev-qa-db-ja.com

IllegalStateExceptionを回避するために、一時停止時にAsyncTask onPostExecuteを処理する方法

ローテーションの変更に関するAsyncTaskに関する多数の投稿に感謝します。互換性ライブラリを使用してDialogFragmentonPostExecuteを削除しようとすると、次の問題が発生します。

進行状況DialogFragmentを表示するAsyncTaskを起動するフラグメントがあり、onPostExecuteでダイアログを閉じてから、別のDialogFragmentをスローする可能性があります。

進行状況ダイアログが表示されているときにアプリケーションをバックグラウンドにすると、フラグメントについて次のようになります。

1)onPause

2)onSaveInstanceState

3)ダイアログを閉じて起動しようとするonPostExecute

アクティビティがその状態を保存したときにトランザクションを効果的にコミットしようとしているので、IllegalStateExceptionを取得します。これは理解しています。

ローテーションでは、アクティビティが再作成されるまでonPostExecuteを取得しないと(おそらく誤って)想定しました。ただし、アプリケーションをバックグラウンドに置くとき、フラグメント/アクティビティが一時停止している間はonPostExectuteが呼び出されないと仮定しました(間違いなく間違っています)。

私の質問は、フラグメント/アクティビティが一時停止していることをonPostExecuteで単に検出し、代わりにonResumeで行う必要があることを単に実行する私のソリューションですか?私にはややいようです。

よろしくお願いします、ピーター。

編集1

2.1以上をサポートする必要がある

編集2

FragmentTransaction:addFragmentTransaction:commitAllowingStateLossを使用してダイアログを表示することを検討しましたが、これには問題がないわけではありません。

37
PJL

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

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

16
Malcolm

必要なものを達成する別の方法は、この記事で説明した )でPauseHandlerクラスを実装することです

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

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

8

BroadcastReceiverを使用するよりも、グアバ、オットー、イベントバスなどのバスライブラリを使用することを好みます。そのパフォーマンスは、放送受信機の実装よりもはるかに優れています。

3
Oguz Ozcan

主要な回避策なしでこの問題の解決策を思い付きました:進捗ダイアログと非同期タスクを維持する基本的な考え方は、この blogentry (もちろんAsyncTaskComplex-Versionを使用しました)で説明されています。すべてのクレジットはこのブログエントリの著者に送られますが、ほんの少しだけ追加しました。

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

2番目の調整は重要な調整であり、IllegalStateExceptionの問題も解決します。

OnRetainCustomNonConfigurationInstance()のasynctaskにこれ以上アクティビティがないことを伝えるだけでなく、onPause()でも実行します。また、onCreate()のasynctaskに新しいアクティビティがあることを伝えるだけでなく、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();
    }
}

}

2

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

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

2
dangel