重い腰をあげてLoaderを使ってみた(とりあえずinitLoaderだけ)

重い腰をあげてLoader触ってみました。

これまでもAsyncTaskはやめろ、Loader(AsyncTaskLoader)使えっていう話は知ってはいたんですが、Loader使い方よく分からんって敬遠してたんですよね。

とりあえずAsyncTaskLoaderを使ってみてわかったこと、感じたことを書いてみたいと思います。

参考にしたところ

サンプルはGitHubに上げてます。

未だによく分かってないところもあるんですが(キャンセル処理についてはまだ手を付けていない)、とりあえず現状で分かったことを書いてまとめます。

ちなみにソースコード読んで動きを把握したいなら、サポートパッケージではなくandroid.app.LoaderManager、android.content.Loaderを使った方がいいと思います。

LoaderManagerの動きを知る

getLoaderManager.initLoaderを呼ぶより前に、LoaderManager.enableDebugLogging(true);を実行すると、LoaderManagerがログを出力してくれるようになるので便利。

LoaderManagerがLoaderの状態を管理しているので、ActivityやAsyncTaskLoaderは非同期処理がどんな状態にあるのか気にしなくて済むのがいいですね。

ただしちゃんと動くようにするためには、Loder側でどういう状態の時にどのメソッドが呼び出されるのかを理解しておく必要があります。そのためにはLoaderManagerの動きを知っておかないとわけが分からないというわけです。

ついでに言うと、メソッド名から想定したイメージと実際の動きの間が、私の感覚と違っていて余計に混乱したというのもあります。

getLoaderManager().initLoader

getLoaderManager(もしくはgetSupportLoaderManager).initLoaderは指定したIDのLoaderがなければLoaderを作成、既に存在していればActivityへのCallbackを設定します。

Loaderを初期化するメソッドというより、コールバックを更新するものと思った方が理解しやすい気がします。私はずっとこのメソッドでLoader作って非同期処理を開始するものだとばかり思っていて、ずっと混乱していました。

指定されたIDのLoaderがまだ存在しない場合は、ActivityのonCreateLoaderにコールバックを行い、ここでLoaderを作ります。

Loaderを作るのはActivityのお仕事です。initLoaderだけなら、onCreateLoaderが呼ばれるのはActivityがonCreateされたとき(画面回転時は除く)だけです。

Loader.onReset

名前からして再稼働させた時に呼ばれるのかと思って混乱しました。いまだによく分かっていません。

onResetが呼ばれたLoaderは再利用されることはない・・・であってると思うんですけど、自信がありません。

LoaderManagerは、LoaderのIDごとに現在動いているLoader、以前に使ってたLoaderを管理しているだけなのようです。だから以前使ったLoaderのインスタンスを再活用したりはしてないと思います。

Loaderで使ってたリソースを解放してねってタイミングのようです。

バックグラウンド処理を引き継げる

とりあえずやってみて感じたのは、AsyncTaskと違ってバックグラウンドの処理を引き継げることがいいなと感じました。

AsyncTaskだと画面回転したらまた最初からやり直しになってたものが、そのままバックグラウンド処理は続いてくれるし、結果もそのまま受け取れるのが素敵。

Loader側でキャッシュ機構を持たせることで、ムダな非同期処理を防ぐことができるのもいいなと思います。

Loader側が非同期処理だけに専念できる

AsyncTaskと違って呼び出し元のActivity(Fragment)が生きてるかどうかを確認しなくていいのが想像以上にやりやすいです。

AsyncTaskだとonPostExecuteで処理結果をUIに反映します。バックグラウンド処理をしている最中に画面回転が生じるとUI更新しようとするものの、対象のActivityは既にお亡くなりになっているせいでアプリが落ちてました。

LoaderではUIの更新について何1つ考える必要がないので、とてもスッキリします。

でも途中経過を伝えられない

AsyncTaskLoaderには途中経過を通知するメソッドが標準で用意されていません。そのためバックグラウンド処理の途中経過を表示することができません。同じAsyncTaskがつくのに別物と

対策としてはAsyncTaskLoaderにActivityへの参照を持たせて通知するみたいなやり方がありましたけど、そんなことするとLoader使う意味が無い気がします。せっかくの非同期処理とUI処理を分離するための機構が台無しです。

LocalBroadcastで対応するっていう策も見つけましたが、こっちの方がまだましかなと思います。

しかし、いっそのことプログレス表示しないという選択肢もありなのかもしれません。データが全部で10件あって、そのうち◯件処理済みとか表示するのではなく、データ読み込み中ですよって表示するだけ。そういう方向での切り替えをした方が通知のための仕組みを実装するより安全な気がします。

Amazonのほしいものリストを公開しています。仕事で欲しいもの、単なる趣味としてほしいもの、リフレッシュのために欲しいものなどを登録しています。 寄贈いただけると泣いて喜びます。大したお礼はできませんが、よりよい情報発信へのモチベーションに繋がりますので、ご検討いただければ幸いです。