ユーザーのアクションに応答するアプリケーションを作成する慣習では、すべての重い操作は個別のスレッドで実行し、何らかの形でユーザーに進捗を通知することをお勧めします。
Androidにはこのアプローチを整理するための多くの方法が含まれていますが、最も便利な方法の1つはAsyncTaskとProgressDialogの使用です。
このカップルは問題を完全に解決しますが、このロジックでのアクティビティの量が1を超えると耐え難い痛みをもたらし始め、制御コードの繰り返しにつながり、アプリケーションが画面の向きの変更をサポートする必要がある場合はさらに大きな痛みをもたらします。
AsyncTask
AsyncTaskに慣れていない人のために、これは実装用のメソッドセットを提供する特別な抽象クラスであることを説明します。
- 初期化コード(UIスレッド)を配置するonPreExecute
- 別のスレッドで実行される重いコードを収容するdoInBackground
- 進行状況レポートのonProgressUpdate (UIスレッド)
- 処理結果のonPostExecute返さ doInBackground(UIスレッド)
- 誰かがタスクをキャンセルしたかどうかを調べるisCancelled
- その後の呼び出しでUIスレッドの進捗状況についてのメッセージを翻訳するためのpublishProgress onProgressUpdate
問題の本質
言及されたクラスを使用することは難しくありません。コードが正常に機能するには、ほんの2、3のスニペットで十分であり、ProgressDialogはタスクの進行状況を通知し始めます。 しかし、ご存知のように、悪魔は細部に宿るので、ダイアログが消えるのに合わせて画面の向きを変更するだけでなく、長いが信じられないほど責任のある操作の結果も必要です。
その理由は、アクティビティのライフサイクルです。画面の向きを変更すると、構成の変更として解釈され、アクティビティが再作成されます。 もちろん、
android:configChanges="orientation"
タグをActivityに設定し、必要に応じて必要な変更を加える独自のコードを定義することにより、このメカニズムを無効にすることができます。 しかし、これは不合理な実装になります。
解決策は、Activity-AsyncTask-ProgressDialogバンドルを管理するための特別なクラスを作成することです。これをAsyncTaskManagerと呼びましょう。
アクティビティ
したがって、理想的には、アクティビティは5つのこと(サンプルプロジェクトのコード)のみを実行する必要があります。
- onCreateメソッドでAsyncTaskManagerを作成します
mAsyncTaskManager = new AsyncTaskManager(this, this);
- 状態からAsyncTaskManagerタスクの回復を委任する
mAsyncTaskManager.handleRetainedTask(getLastNonConfigurationInstance());
- 特定のタスクを作成し、AsyncTaskManagerに渡します
mAsyncTaskManager.setupTask(new Task(getResources()));
- タスクを状態に保存するAsyncTaskManagerを委任する
return mAsyncTaskManager.retainTask();
- 非同期タスク完了を処理する
public interface OnTaskCompleteListener { void onTaskComplete(Task task); }
メソッドのパラメーターでは、実行が完了したタスクが転送されます。
AsyncTaskManager
AsyncTaskManagerは、すべてのコンポーネントの正しい動作を担当する必要があります。これは、次のタスクのリストに要約されます。
- 初期化中にダイアログを作成する
- 管理でタスクを受け取ったら、それを実行します
- ダイアログにタスクステータスを表示する
- 状態に保存した後にタスクから切断し、復元時に再接続します
- ダイアログをキャンセルするときにタスクをキャンセルします
- タスク完了時にダイアログを閉じる
- タスクの完了またはキャンセルをアクティビティに通知
public interface IProgressTracker { void onProgress(String message); void onComplete(); }
実装:
@Override public void onProgress(String message) { if (!mProgressDialog.isShowing()) { mProgressDialog.show(); } mProgressDialog.setMessage(message); } @Override public void onComplete() { mProgressDialog.dismiss(); mAsyncTask.setProgressTracker(null); mTaskCompleteListener.onTaskComplete(mAsyncTask); mAsyncTask = null; }
タスクへの参加:
mAsyncTask.setProgressTracker(this);
タスクから切り離す:
mAsyncTask.setProgressTracker(null); mAsyncTask = null;
キャンセルダイアログ:
@Override public void onCancel(DialogInterface dialog) { mAsyncTask.setProgressTracker(null); mAsyncTask.cancel(true); mTaskCompleteListener.onTaskComplete(mAsyncTask); mAsyncTask = null; }
AsyncTaskManagerは、作業タスクを再作成されたアクティビティのインスタンスに接続および切断する一種のキーとして機能します。 さらに、ProgressDialogを操作するロジックを引き継いで非表示にします。
AsyncTask
タスクでは、メインメソッドの実装に加えて、AsyncTaskManagerとの接続/切断に役立つメソッドの実装が必要です。
public void setProgressTracker(IProgressTracker progressTracker) { mProgressTracker = progressTracker; if (mProgressTracker != null) { mProgressTracker.onProgress(mProgressMessage); if (mResult != null) { mProgressTracker.onComplete(); } } }
上記のコードからわかるように、タスクは計算結果と最後の進捗メッセージを保存し、状態に応じて1つまたは別のトラッカーメソッド(AsyncTaskManager)を呼び出します。
したがって、アクティビティが再作成される前にタスクが完了した場合でも、タスクの完了に関する通知を受け取ります。
結果
今、あなたはあなたの手で電話を大胆にひねることができます-すべてのタスクは正しく処理されます。
このようなマネージャーを使用すると、Activityのコードの量が大幅に削減され、プロジェクトでこの機能を再利用できます。 最近のアプリケーションでこのアプローチを開発し、正常に適用しました。
参照資料
ドラフト例を使用したアーカイブ
AsyncTaskの説明(en)
ストリームの簡単な作業(en)
Androidのダイアログ(en)