package com.android_mvc.framework.task; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import android.app.ProgressDialog; import android.content.Context; import com.android_mvc.framework.annotations.SuppressDebugLog; import com.android_mvc.framework.common.FWUtil; /** * 複数の非同期タスクやイベントを逐次実行するためのコンテナ。 * * 利用できるのはUIスレッド上のみなので注意。本クラスを入れ子で利用することはできない。 * もしUIスレッド上で別のAsyncTasksRunnerを呼び出したい場合は, * whenAllTasksCompleted() メソッド内のコールバック処理として記述すること。 * @author id:language_and_engineering */ @SuppressDebugLog public class AsyncTasksRunner { // 実行したい非同期タスク達 private ISequentialRunnable[] tasks; // 現在取り扱い中のタスクのインデックス private int executing_task_cursor = 0; // 全タスクから返されるデータのストア private HashMap<String, Object> data_from_all_tasks = new HashMap<String, Object>(); // ダイアログ表示フラグ private boolean requireDialogFlag = false; // ダイアログ上に表示する文言 private String dialogMessage = null; // ダイアログ private ProgressDialog dialog = null; // ダイアログ用のコンテキスト private Context context; // 全タスク完了時のコールバック private RunnerFollower follower; /** * 初期化 * @param iSequentialRunnables 逐次実行したい非同期タスクの配列 */ public AsyncTasksRunner( ISequentialRunnable[] iSequentialRunnables ) { this.tasks = iSequentialRunnables; // TODO: 初期化の時点でContextを控えておいたほうがいいかもしれない。 } // ---------------- タスクの実行 ----------------- /** * 全タスクを実行開始 */ public void begin() { FWUtil.d("begin()が呼ばれました。"); if( tasks.length > 0 ) { FWUtil.d("最初のタスクの実行を開始します。"); if( requireDialogFlag ) { // ダイアログ表示 dialog = new ProgressDialog( context ); dialog.setMessage( dialogMessage ); dialog.show(); } executeCurrentTask(); } FWUtil.d("begin()を終了します。"); } /** * 現在のタスクを実行 */ private void executeCurrentTask() { FWUtil.d("現在のタスクを実行します。インデックスは" + executing_task_cursor); getCurrentTask().kickByRunner( this ); } /** * 次のタスクを実行 */ private void executeNextTask() { executing_task_cursor ++; executeCurrentTask(); } // ---------------- タスクの完了 ----------------- /** * 現在のタスクの実行完了時に,タスク側から呼ばれる */ public void onCurrentTaskFinished() { // 現行タスクの終了処理 mergeDataFromTask( getCurrentTask() ); if( mustMoveToNextTask() ) { FWUtil.d("次のタスクへ移動する事を決定"); // 次のタスクへ移動 executeNextTask(); } else { FWUtil.d("次のタスクへ移動しない事を決定"); onAllTasksFinished(); } } /** * 全タスクの実行が完了した際の内部処理。 */ private void onAllTasksFinished() { // ダイアログの後始末 if( requireDialogFlag && ( dialog != null )) { // ダイアログを消す dialog.dismiss(); FWUtil.d("ダイアログを消しました。"); } // ユーザ定義コールバック if( this.follower != null) { // NOTE: これがメイン(UI)スレッドで実行されるところがミソ。 // 再度AsyncTaskの生成に取り掛かれるから。 FWUtil.d("ユーザ定義のコールバックを実行します。"); follower.exec(); FWUtil.d("ユーザ定義のコールバックを実行完了。"); } FWUtil.d("全非同期タスクの逐次実行が完了。"); } /** * 全タスク終了時のユーザ側コールバックを定義 */ public AsyncTasksRunner whenAllTasksCompleted(RunnerFollower follower) { this.follower = follower; return this; } // ---------------- 個別のタスクの管理 ----------------- /** * 現在取り組み中のタスクを返す */ private ISequentialRunnable getCurrentTask() { return tasks[ executing_task_cursor ]; } /** * 現在処理中のタスクが最後のタスクかどうか返す */ private boolean isProccessingLastTask() { return ( tasks.length == ( executing_task_cursor + 1 ) ); } /** * タスクを継続可能かどうか返す */ private boolean mustMoveToNextTask() { // 最後のタスクでなく,現行タスクの結果がOKであれば。 return ( ( ! isProccessingLastTask() ) && ( getCurrentTask().tasksContinuable() ) ); } /** * 1タスクの保持データを回収 * @param iSequentialRunnable 実行し終わった非同期タスク */ private void mergeDataFromTask( ISequentialRunnable iSequentialRunnable ) { // HashMapを取り出して,そのループの準備をする HashMap<String, Object> data_from_current_task = iSequentialRunnable.getStoredObjects(); Set<String> keySet = data_from_current_task.keySet(); Iterator<String> keyIterator = keySet.iterator(); // 全キーについて,個別タスクからランナー側に値を取り込む while( keyIterator.hasNext() ) { // 1ペアを読み込み String key = (String)keyIterator.next(); Object value = data_from_current_task.get( key ); // 1ペアを上書き data_from_all_tasks.put( key, value ); FWUtil.d("ランナー側で," + key + "の値を上書きしました。"); } } /** * これまでに実行した全タスクから回収した値をキーごとに返す */ public Object getDataByKey( String key ) { return data_from_all_tasks.get( key ); } // -------------- ダイアログ -------------- /** * 全ての非同期処理が終わるまで,プログレスダイアログを表示する * @param string ダイアログ上の表示文字列 */ public AsyncTasksRunner withSimpleDialog(String s, Context context ) { this.context = context; setRequireDialogFlag(true); setDialogMessage(s); return this; } /** * 非同期処理の間,ダイアログを表示しない。 */ public AsyncTasksRunner withoutDialog() { setRequireDialogFlag(false); return this; } private void setDialogMessage(String s) { dialogMessage = s; } private void setRequireDialogFlag(boolean b) { requireDialogFlag = b; } }