initLoader()とrestartLoader()のどちらを使うか

場合によってはrestartLoader()のみを使っても良さそう、というお話です。

Androidアプリにおいて、例えばデータベースからデータを非同期で読み込み、ListViewなどへ読み込んだデータを反映するときなどは、ローダーという機構を使用することができます。

そのローダーを使用するときの手順は、ざっくりと書けば下記のようになります。

  1. getLoaderManager()を使ってLoaderManagerのインスタンスを取得し、
  2. 取得したLoaderManagerのinitLoader()メソッドでローダーを初期化する。

この手順をLoaderCallbacks()インターフェースを実装したクラスに書けば、データ取得の実行段階に応じて、下記の各々のメソッドが自動的に呼ばれます。

  • onCreateLoader()…ローダーを最初に使うときに呼ばれる。
  • onLoadFinished()…データの読み込みが完了したときに呼ばれる。
  • onLoaderReset()…ローダーがリセットされたときに呼ばれる。

ところで、先にも書いたとおりローダーの初期化にはinitLoader()を実行するのですが、似たようなメソッドにrestartLoader()というのもあります。どちらもローダー開始機能を有しているのは同じですが、両者の違いは既存のローダー(同じIDのローダー)が存在していた場合に現れます。既存のローダーを「再利用する」のか、「破棄してローダーを再作成する」のかという違いです。

  • initLoader()…再利用する
  • restartLoader()…破棄してローダーを再作成する

この両者の機能の違いを勘案して、適宜initLoader()とrestartLoader()を使い分ければいいとは思うのですが、最初からrestartLoader()だけを使ってもよいのではないかという思いが私の頭によぎりました。ただ、そのとき懸念したことは、initLoader()とrestartLoader()の間に何らかの依存関係があり、それ故initLoader()のコールが必須なのかもしれないということです。

そこで、実際にinitLoader()とrestartLoader()の内部処理が記述されているソースコードを読み込んでみました。SDKに含まれているLoaderManager.javaというソースファイルに記述されています。実際にローダーを作成して返却している箇所を抜き出してみると…

initLoader()

info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
// 中略
return (Loader<D>)info.mLoader;

restartLoader()

info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
return (Loader<D>)info.mLoader;

ということで、両者とも結局はLoaderをクリエイトして返却していることは同じであり、各々の引数に設定するデータもreturnに至るまでの処理を見たところ、ローダーの新規作成時については同じようです。違いは、restartLoader()の方で分岐条件にてローダーを破棄する処理が記述されていること、initLoader()の方でローダーがあればそれをreturnしていることで、まあ仕様通りといえばその通りですが、ここではinitLoader()とrestartLoader()に直接の依存関係がない事が分かりましたので、その点だけでもソースを調べた甲斐がありました。

よって、ローダー再利用の必要がなければ、restartLoader()のみでローダー処理を記述しても問題ないように見えます。逆に言えば、例えば何らかのアクションで変更されたデータをListViewへ瞬時にリアルタイムに反映したい場合など、またローダーが再利用されたら困るような機能を実現したい場合などは、最初からrestartLoader()のみを使用する方が簡便かもしれません。

論より証拠ということで、最初からrestartLoader()のみを使用してデータベースから取得したデータをListViewに反映させてみたところ、想定通りに動作させることができました。