今更ながら入門する AsyncTaskLoader
今更ながらAsyncTaskLoaderを触る機会があったので挙動をメモしておく
基本的な使い方
基本的な実装は
とか
Tumbling Dice — [Android]AsyncTaskLoaderをもっと便利にする(準備編)
挙動メモ的なこと
そもそも Google様自体が CursorLoader というのを作りたいために作った仕組みらしい。*1
- 基本処理中はメモリ常駐する。
- Activityが消えても処理中は処理を続行できる仕組み
ただLoaderから再作成時に引数なしで作成される *2
- => この時に引数無しでLoaderが作成されるため、Bundleで引数を渡す作り等にしていると挙動が変になる
- => だからBundle無視して、下記の☆みたいな記述を書いているコードが出てくる・・
loaderCallbacks自体が
- よくググるとActivityとか使用クラスでベタにimplementsしてるけど
- Activtyで複数Loaderを使おうとする場合はあり得ない実装
- G様的にUI無しFragmentを作って処理を分けろという設計推奨なのでしょうか?
class ImageLoaderCallback implements LoaderManager.LoaderCallbacks<Bitmap> { @Override public Loader<Bitmap> onCreateLoader(int id, Bundle args) { return new HttpAsyncImageLoader(getContext(), this.mUrl);//☆ } @Override public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) { setImageBitmap(data); } @Override public void onLoaderReset(Loader<Bitmap> loader) { } }
使いづらいこと
publishProgress => AsynkTask:onProgress
みたいな仕組みがない。
これググると、
- mActivity.runOnUiThread を書く
- ただ普通createに書けない
- restartLoaderセット+カスタムセッター+forceLoad でActivityの参照を渡す
- loaderCallbacks側等にstatic Handler変数を容易して
- sendMessageで送れ
- loaderCallbacksの
- createLoader でProgresslg開始
- loadfinished でProgressDlg終了
でええんんやない?
とくに3つ目がG様推奨パターンで
- 3番めみたいなインナークラスどうせ書くなら
- RxJavaで書きなおせばいいよね
というのが最新の流行りになっている。
だからやたらと
なアプリが乱造されている気がする。
複数本APIをまとめる書き方は
1ユーザとしては凄くストレスたまるんだけど、皆さんどうなんでしょう?
結論的なこと
イベントトリガー関数等で
getSupportLoaderManager::restartLoader
を呼び出すのがベストかなと。
通常のサンプルみたいな使い方で
- Activity::onCreateで getSupportLoaderManager::initLoader
- イベントトリガー関数で getSupportLoaderManager::forceLoadを呼び出す
とかやると
- onLoadFinishedが二度実行されたり
- 前回の実行状態が残っていたり
- 変に状態が残っているためにクラッシュしたり
わけわからない挙動をする。
そのための対処方法をググると
上記方法でやりつつ、
- Loader内部側で呼び出す度に、内部で初期化(stopしてstart)とかしているコードがあったりしますが
- 実行中か判定して再初期化するなら、restartLoaderでよくない?
- 再実行の時って前回の状態っていらないでしょう?
例外的な対処
loaderCallbacks に渡せる引数は、標準だとBundleのみなので
PraceableやSerialize形式等の強制で引数を渡しにくいものに限って*4
その場で 下記の形式で実行させるイメージかなと*5
- restartLoader
- カスタムセッタークラス
- forceLoad()
クラッシュ対策
Activityを終了してもLoaderがどうも残る仕様らしい。
実行中でなければ Activity::onStart() で破棄する必要がある*6
あと仕組み的に下記が想定動作らしいので
- AsyncTaskLoaderを使ったActivityに戻ってきたとき*7
- onStartLoaderが呼ばれることがAsyncTaskLoaderを使う目的
- onLoadFinishedでdestroyLoaderしてしまう実装はナンセンス*8
勿論処理中であればそのまま実行させるべきだとは思う。
loader.isRunnnig() は勿論処理中を表す、カスタム関数です
勿論忘れがちですが、処理が終了していたらProgressDialog等のインジケータも消すべきです
- HogeActivity::onStart
if(loader != null && !loader.isRunnnig()){ loader =null; }
- HogeLoader
public static class HogeLoader extends AsyncTaskLoader<ResultClass> { private boolean bRunnning = false; public HogeLoader(Context context) { super(context); bRunnning = false; } @Override public void deliverResult(ResultClass data) { // Loderが処理した結果を返す。(メインスレッドで実行される) super.deliverResult(data); } @Override public ResultClass loadInBackground() { bRunnning = true; //[TODO] Loderが実行するバックグラウンド処理 bRunnning = false; return "abc"; } @Override protected void onStartLoading() { // Loder側の準備ができたタイミングで呼び出される // UIスレッドで実装される forceLoad(); } // カスタムセッターの実装例 // Bundle形式だとMapとかは渡せない。このあとforceLoadを手動で呼ぶ private Map<String,CustomClass> param; public void setParam(Map<String,CustomClass> args){ param = args; } public boolean isRunning(){ return bRunnning; } }
AsyncTaskLoaderのテスト
後で試す。メモ