ProgressDialogとAsyncTaskの併用を説明する良い
記事がHabrで既に公開されています。ここでは、同様の機能をフラグメントで、より正確に
DialogFragmentと
AsyncTaskLoaderを使用して達成する方法を説明します。
目標は:
- 長い操作を実行するときにProgressDialogを表示します。メッセージテキストはタスクの進行状況を通知できます。
- アプリケーションによる方向変更の正しいサポート。

実装
1.非同期プロセスからのメッセージを、AsyncTaskLoaderの後継であるAbstractTaskLoader抽象クラスに送信します。
public abstract class AbstractTaskLoader extends AsyncTaskLoader<Object> {
さらに、いくつかの長い操作を実行するクラスは、この抽象クラスから既に継承されており、タスクを実行するプロセスについて通知できます。
@Override public Object loadInBackground() { … publishMessage(result); … }
2.次のステップは、メッセージを表示するProgressDialogの実装であり、画面の向きを変更するときにローダーを正しく起動/再起動します。 このクラスには、ローダーとダイアログのコールバックハンドラも含まれています。
public abstract class AbstractTaskProgressDialogFragment extends DialogFragment implements LoaderCallbacks<Object>, OnCancelListener { private ProgressDialog progressDialog; private final AbstractTaskLoader loader; protected abstract void onLoadComplete(Object data); protected abstract void onCancelLoad(); protected AbstractTaskProgressDialogFragment(AbstractTaskLoader loader,String message){ loader.setHandler(handler); this.loader = loader; Bundle args = new Bundle(); args.putString("message", message); setArguments(args); } @Override public ProgressDialog onCreateDialog(Bundle savedInstanceState) { String message = getArguments().getString("message"); progressDialog = new ProgressDialog(getActivity()); progressDialog.setMessage(message); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDialog.setOnCancelListener(this); progressDialog.show(); return progressDialog; } private final Handler handler = new Handler() { public void handleMessage(Message msg) { if(msg.what == AbstractTaskLoader.MSGCODE_MESSAGE){ String message = AbstractTaskLoader.getMessageValue(msg); if(message!=null){
クラスも抽象化されますが、これは必須ではなく、最小限のバインディングを実装するためのオプションの1つにすぎません。 作業の結果のみが重要です。
onLoadComplete(オブジェクトデータ);
onCancelLoad();
そして、これらのメソッドを後継機に実装します(インターフェースを介して行いました。以下を参照)。
ここでは、ローダーがコンストラクターに渡され、コンストラクターが実行され、起動時にテストメッセージがすぐに表示されます。 ハンドラーは、進行状況に関する情報メッセージをローダーから送信するために使用されます。 onCreateDialogで、ダイアログを作成して表示します。
次に、画面の向きを変更しても、このフラグメントはその状態を保持することを示す必要があります。 これは
setRetainInstance(true)メソッドを使用して行われ
ます。この場合、画面の向きが変更されたときにonDestroyは呼び出されず、ダイアログは閉じません。
@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setRetainInstance(true);
onActivityCreatedメソッドを再定義します。このメソッドでは、最初の起動時にローダーを起動するか、既存のローダーに再接続します(画面の向きを変更する場合)。
小さな余談:setRetainInstanceメソッドは、「
compatibility-library 」とペアになったDialogFragmentに対して正確に動作しません。つまり、onDestroyメソッドは呼び出されますが、実行されるべきではありません。 ただし、回避策があります。ダイアログに追加します。
@Override public void onDestroyView() { if (getDialog() != null && getRetainInstance()) getDialog().setOnDismissListener(null); super.onDestroyView(); }
詳細は
こちら 。
ローダーの起動およびシャットダウン処理:
@Override public Loader<Object> onCreateLoader(int id, Bundle args) { loader.forceLoad(); return loader; } @Override public void onLoadFinished(Loader<Object> loader, Object data) { onLoadComplete(data); ((AbstractTaskLoader) loader).setHandler(null); hideDialog(); }
ユーザーキャンセル処理-「戻る」ボタンを押す:
@Override public void onCancel(DialogInterface dialog) { super.onCancel(dialog); loader.cancelLoad();
3.ベースフレームが作成されました。これで使用します。
完了通知用のインターフェースを作成します。
public interface ITaskLoaderListener { void onLoadFinished(Object data); void onCancelLoad(); }
作成した抽象クラスに基づいてProgressDialogの実装を作成します。
public class TaskProgressDialogFragment extends AbstractTaskProgressDialogFragment { private ITaskLoaderListener taskLoaderListener; private final Handler handler = new Handler(); protected TaskProgressDialogFragment(AbstractTaskLoader loader, String message) { super(loader, message); } protected void setTaskLoaderListener(ITaskLoaderListener taskLoaderListener){ this.taskLoaderListener = taskLoaderListener; } @Override protected void onLoadComplete(final Object data) { if(taskLoaderListener!=null){ taskLoaderListener.onLoadFinished(data); } } @Override protected void onCancelLoad() { if (taskLoaderListener != null) { taskLoaderListener.onCancelLoad(); } } ...
Loaderを便利に構築して実行するBuilder:
public static class Builder { FragmentActivity fa; AbstractTaskLoader loader; ITaskLoaderListener taskLoaderListener; Boolean cancelable; String progressMsg; public Builder(FragmentActivity fa, AbstractTaskLoader loader, String progressMsg) { this.fa = fa; this.loader = loader; this.progressMsg = progressMsg; } public Builder setTaskLoaderListener( ITaskLoaderListener taskLoaderListener) { this.taskLoaderListener = taskLoaderListener; return this; } public Builder setCancelable(Boolean cancelable) { this.cancelable = cancelable; return this; } public void show() { String TAG_FRAGMENT = UUID.randomUUID().toString();
4.最後に、これがローダーです。 たとえば、ProgreesDialog-eのメッセージテストを毎秒更新するだけです。
public class SampleTask extends AbstractTaskLoader { @Override public Object loadInBackground() { String result = ""; for(int i=0; i<10;i++){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } result = "Wait: " + String.valueOf(i); publishMessage(result); if(isCanselled()){ break; } Log.d(TAG, result); } return result; }
開始に便利な静的メソッドを追加します。
public static void execute(FragmentActivity fa, ITaskLoaderListener taskLoaderListener) { SampleTask loader = new SampleTask(fa); new TaskProgressDialogFragment.Builder(fa, loader, "Wait...") .setCancelable(true) .setTaskLoaderListener(taskLoaderListener) .show(); }
5.チャレンジ
public class SampleActivity extends FragmentActivity implements ITaskLoaderListener{ … SampleTask.execute(this, this); @Override public void onCancelLoad() { Log.d(TAG, "task canceled"); } @Override public void onLoadFinished(Object data) { if(data!=null && data instanceof String){ Log.d(TAG, "task result: " + data); } }
サンプルのソースコードを
ダウンロードしてください 。建設的な批判やコメントを歓迎します。
ソース:
1. Evelina Vrabieブログ、記事
「Androidフラグメント、保存状態と画面回転」2.スタックオーバーフロー