236

バックグラウンド スレッド (私は を使用AsyncTask) でインターネットからいくつかのデータをダウンロードし、ダウンロード中に進行状況ダイアログを表示します。向きが変わり、アクティビティが再起動され、AsyncTask が完了します。進行状況ダイアログを閉じて、新しいアクティビティを開始したいと考えています。ただし、dismissDialog を呼び出すと、例外がスローされることがあります (おそらく、Activity が破棄され、新しい Activity がまだ開始されていないため)。

この種の問題 (ユーザーが向きを変えても機能するバックグラウンド スレッドから UI を更新する) を処理する最善の方法は何ですか? Google の誰かが「公式の解決策」を提供しましたか?

4

8 に答える 8

337

ステップ #1:内部 (静的ではないネストされた) クラスではなくAsyncTaskstaticネストされたクラス、または完全に別のクラスにします。

ステップ #2:コンストラクターとセッターを介して設定されたデータ メンバーを介してAsyncTask保持します。Activity

ステップ #3: を作成するときに、コンストラクターAsyncTaskに電流を供給します。Activity

ステップ #4: では、元の現在離れているアクティビティから切り離した後onRetainNonConfigurationInstance()、 を返します。AsyncTask

ステップ #5:onCreate()で、 でない場合getLastNonConfigurationInstance()null、それをクラスにキャストしAsyncTask、セッターを呼び出して、新しいアクティビティをタスクに関連付けます。

ステップ #6: からアクティビティ データ メンバーを参照しないでくださいdoInBackground()

上記のレシピに従えば、すべてうまくいきます。onProgressUpdate()およびは、後続のonPostExecute()の開始と終了の間で中断されます。onRetainNonConfigurationInstance()onCreate()

この手法を示すサンプル プロジェクトを次に示します。

もう 1 つのアプローチは、 を捨てAsyncTaskて作業を に移動することIntentServiceです。これは、実行する作業が長くなる可能性があり、ユーザーがアクティビティに関して何を行うかに関係なく続行する必要がある場合 (たとえば、大きなファイルのダウンロード) に特に役立ちます。順序付けされたブロードキャストIntentを使用して、実行中の作業にアクティビティを応答させるか (まだフォアグラウンドにある場合)、を発生Notificationさせて、作業が完了したかどうかをユーザーに知らせることができます。このパターンの詳細については、こちらのブログ投稿をご覧ください。

于 2010-09-29T13:23:27.757 に答える
13

受け入れられた回答は非常に役に立ちましたが、進行状況ダイアログはありません。

読者の皆さんにとって幸いなことに、進行状況ダイアログを備えた AsyncTask の非常に包括的で実用的な例を作成しました。

  1. 回転が機能し、ダイアログが存続します。
  2. 戻るボタンを押すと、タスクとダイアログをキャンセルできます (この動作が必要な場合)。
  3. フラグメントを使用しています。
  4. デバイスが回転すると、アクティビティの下にあるフラグメントのレイアウトが適切に変更されます。
于 2012-09-06T15:42:35.020 に答える
9

マニフェストファイルを編集せずに、このジレンマの解決策を見つけるために1週間苦労しました。このソリューションの前提条件は次のとおりです。

  1. 常に進行状況ダイアログを使用する必要があります
  2. 一度に実行されるタスクは1つだけです
  3. 電話が回転し、進行状況ダイアログが自動的に閉じられたときにタスクを持続させる必要があります。

実装

この投稿の下部にある2つのファイルをワークスペースにコピーする必要があります。次のことを確認してください。

  1. あなたActivityのすべては拡張する必要がありますBaseActivity

  2. onCreate()super.onCreate()によってアクセスする必要のあるメンバーを初期化した後に呼び出す必要がありますASyncTask。また、オーバーライドgetContentViewId()してフォームレイアウトIDを提供します。

  3. onCreateDialog() 通常のようにオーバーライドして、アクティビティによって管理されるダイアログを作成します。

  4. AsyncTasksを作成するための静的内部クラスのサンプルについては、以下のコードを参照してください。結果をmResultに保存して、後でアクセスできます。


final static class MyTask extends SuperAsyncTask<Void, Void, Void> {

    public OpenDatabaseTask(BaseActivity activity) {
        super(activity, MY_DIALOG_ID); // change your dialog ID here...
                                       // and your dialog will be managed automatically!
    }

    @Override
    protected Void doInBackground(Void... params) {

        // your task code

        return null;
    }

    @Override
    public boolean onAfterExecute() {
        // your after execute code
    }
}

そして最後に、新しいタスクを起動します。

mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();

それでおしまい!この堅牢なソリューションが誰かに役立つことを願っています。

BaseActivity.java(インポートを自分で整理する)

protected abstract int getContentViewId();

public abstract class BaseActivity extends Activity {
    protected SuperAsyncTask<?, ?, ?> mCurrentTask;
    public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(getContentViewId());

        mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
        if (mCurrentTask != null) {
            mCurrentTask.attach(this);
            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
        mCurrentTask.postExecution();
            }
        }
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
    super.onPrepareDialog(id, dialog);

        mDialogMap.put(id, true);
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (mCurrentTask != null) {
            mCurrentTask.detach();

            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
                return mCurrentTask;
            }
        }

        return super.onRetainNonConfigurationInstance();
    }

    public void cleanupTask() {
        if (mCurrentTask != null) {
            mCurrentTask = null;
            System.gc();
        }
    }
}

SuperAsyncTask.java

public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
    protected BaseActivity mActivity = null;
    protected Result mResult;
    public int dialogId = -1;

    protected abstract void onAfterExecute();

    public SuperAsyncTask(BaseActivity activity, int dialogId) {
        super();
        this.dialogId = dialogId;
        attach(activity);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mActivity.showDialog(dialogId); // go polymorphism!
    }    

    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        mResult = result;

        if (mActivity != null &&
                mActivity.mDialogMap.get((Integer) dialogId) != null
                && mActivity.mDialogMap.get((Integer) dialogId)) {
            postExecution();
        }
    };

    public void attach(BaseActivity activity) {
        this.mActivity = activity;
    }

    public void detach() {
        this.mActivity = null;
    }

    public synchronized boolean postExecution() {
        Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
        if (dialogExists != null || dialogExists) {
            onAfterExecute();
            cleanUp();
    }

    public boolean cleanUp() {
        mActivity.removeDialog(dialogId);
        mActivity.mDialogMap.remove((Integer) dialogId);
        mActivity.cleanupTask();
        detach();
        return true;
    }
}
于 2011-07-08T03:26:43.383 に答える
4

Google の誰かが「公式の解決策」を提供しましたか?

はい。

ソリューションは、単なるコードではなく、アプリケーション アーキテクチャの提案です。

彼らは、アプリケーションの状態に関係なく、アプリケーションがサーバーと同期して動作できるようにする3 つの設計パターンを提案しました(ユーザーがアプリを終了したり、ユーザーが画面を変更したり、アプリが終了したり、その他すべての可能な状態であっても機能します)。バックグラウンドデータ操作が中断される可能性があります。これでカバーされます)

この提案は、Virgil Dobjanschi によるGoogle I/O 2010でのAndroid REST クライアント アプリケーションのスピーチで説明されています。1時間の長さですが、見ごたえ十分です。

その基本は、ネットワーク操作を、アプリケーション内のService任意のものとは独立して機能する に抽象化することActivityです。データベースを操作している場合、 と を使用するContentResolverと、取得したリモート データでローカル データベースを更新すると、追加のロジックなしで UI を更新するのに便利Cursorなすぐに使えるオブザーバー パターンが得られます。他の操作後のコードは、に渡されたコールバックを介して実行されますService(これにはResultReceiverサブクラスを使用します)。

とにかく、私の説明は実際にはかなり曖昧です。必ずスピーチを見てください。

于 2015-08-08T16:49:31.857 に答える
0

これが私の解決策です: https ://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog

基本的に手順は次のとおりです。

  1. onSaveInstanceStateまだ処理中の場合は、タスクを保存するために使用します。
  2. onCreateタスクが保存されていれば取得します。
  3. 表示されている場合は破棄onPauseします。ProgressDialog
  4. では、タスクがまだ処理中かどうかonResumeを示します。ProgressDialog
于 2015-03-05T21:57:38.970 に答える
0

アクティビティ ハンドラを使用してすべてのアクティビティ アクションを呼び出す必要があります。したがって、スレッドに参加している場合は、Runnable を作成し、Activitie の Handler を使用して投稿する必要があります。そうしないと、致命的な例外でアプリがクラッシュすることがあります。

于 2011-07-01T00:14:08.177 に答える