データをネットワークからダウンロードするなどの時間のかかる処理を行う場合、メインスレッドで行うのではなく別スレッドで非同期に処理するというは避けて通れないものです。
前回はAndroidの非同期処理はAsyncTaskよりもAsyncTaskLoaderを使うのが良さそうだというのを書きましたが、今回はそのAsyncTaskLoaderの使い方についてまとめてみようと思います。
基本的な流れ
AsyncTaskLoaderクラスをベースとしたLoaderクラスを作成し、その中で非同期で行う処理を実装します。AsyncTaskLoaderはジェネリックなクラスで定義されていて処理結果のオブジェクトを自由に定義できます。
Activity (もしくはFragment) 側では、LoaderManager.LoaderCallbacksインターフェースを実装します。このインターフェースを実装する事で非同期処理後の処理などを実装できます。
Activity側の流れ
LoaderManager.initLoaderメソッドを呼び出す
LoaderCallbacksインターフェースの onCreateLoader() が呼び出されます。
▼
onCreateLoaderメソッドで非同期処理のインスタンスを作成する
メソッドの戻り値に作成したインスタンスを返します。
LoaderManagerは適切なタイミングで非同期処理を開始し始めます。
▼
非同期処理が終了したら onLoadFinishedメソッドが呼び出される
非同期処理終了後の処理を実装します。
LoaderManager.destroyLoaderメソッドを呼び出して非同期処理のインスタンスを破棄します。
Loaderクラスの流れ
onStartLoadingメソッドでforceLoadを実行する
LoaderManagerは非同期処理の開始準備が整うと Loader.onStartLoading() が呼び出しされます。
inStartLoadingをオーバーライドして Loader.forceLoad() を実行すると処理を開始します。
▼
別スレッドでloadInBackgroundメソッドが動き始める
loadInBackgroundをオーバーライドして処理を実装します。
Activityのライフサイクルに対応する
大まかな流れは上記の通りですが、それだけでは足りません。
詳しくは「Androidの非同期処理はAsyncTaskよりもAsyncTaskLoaderで」をみて頂ければと思いますが、Activityは画面が回転する時など再構築されてしまいます。
非同期処理を開始したActivityは破棄される可能性があり、再構築後のActivityが非同期処理の終了通知を受け取れるようにする必要があります。
Activityサンプル
public class TestActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<TestTaskLoader.Result> { //Loaderを識別する為のID private static final int LOADERID_TEST = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); LoaderManager lm = getSupportLoaderManager(); //回転などでActivityが再作成された時の為の処理 if (null != lm.getLoader(LOADERID_TEST)) { //LoaderManagerはActivityが再作成されても状態を維持している //LoaderManagerが指定IDのLoaderを持っているなら再作成前にLoaderが実行を開始しているという事なので //再作成後のActivityがonLoadFinishedを受け取る為にinitLoaderをしてLoaderとの関連付けを行う lm.initLoader(LOADERID_TEST, null, this); } } public void onButtonClick(View v) { getSupportLoaderManager().initLoader(LOADERID_TEST, null, this); } @Override public Loader<TestTaskLoader.Result> onCreateLoader(int id, Bundle args) { return new TestTaskLoader(getApplication()); } @Override public void onLoaderReset(Loader<TestTaskLoader.Result> loader) { } @Override public void onLoadFinished(Loader<TestTaskLoader.Result> loader, TestTaskLoader.Result result) { getSupportLoaderManager().destroyLoader(LOADERID_TEST); } }
この例ではボタンが押された時に非同期処理を開始するという想定です。
その為、onButtonClickメソッドで initLoader を呼び出して非同期処理を開始していますが、Activityの再構築に備えて onCreateメソッドで LoaderManager が Loader を持っているか(非同期処理が実行中か)を確認し、持っていれば initLoader を呼び出しています。
AsyncTaskLoaderサンプル
public class TestTaskLoader extends AsyncTaskLoader<TestTaskLoader.Result> { public class Result { public int status; public String message; } private Result mResult; private boolean mIsStarted; public TestTaskLoader(Context context) { super(context); mResult = null; mIsStarted = false; } @Override public TestTaskLoader.Result loadInBackground() { TestTaskLoader.Result result = new TestTaskLoader.Result(); // // ここで非同期の処理をする // return result; } @Override public void deliverResult(Result result) { // 非同期処理の結果を保存しておく mResult = result; super.deliverResult(result); } @Override protected void onStartLoading() { // 呼び出し元のActivityが回転などで再作成されるとinitLoaderを再度呼ばなければならない // initLoaderがよばれるとココに来るが再度forceLoadしてしまうと // 実行中のloadInBackgroundは破棄されもう一度loadInBackgroundが開始されてしまう // 実行中のloadInBackgroundが無い時だけ実行を開始し終了している時は直ちに結果を返す if (null != mResult) { deliverResult(mResult); return; } if ((! mIsStarted) || takeContentChanged()) { forceLoad(); } } @Override protected void onForceLoad() { super.onForceLoad(); mIsStarted = true; } }
コメントをお書きください