Loader について知っておいたほうが良いこと 3 つ

`AsyncTaskLoader`や`CursorLoader`など、`AsyncTask`では不便だったいくつかのことが`Loader`という仕組みによって解決されました。
主には、`AsyncTask`で悩みの種であった`Activity`のライフサイクルとの分離が、`Loader`によって実現されます。これによって、画面回転したときに`AsyncTask`のインスタンスをうまいこと作りなおさないようにしつつも、コールバックを受けるオブジェクトの参照は新しい`Activity`に紐付いたものにしなければならない、ということも、システムが勝手にやってくれるようになるので、管理がとても楽になります。

一方で、結構扱い方には注意が必要なことがあって、実装者が知った上で使用すべきこともいくつかあります。

# Loader のライフサイクル

## `LoaderManager` のインスタンス取得は `Activity#onStart()` より前で呼ぶ

`LoaderManager` は、1 つの`Activity`の中でシングルトンであることが保証されていて、`Activity`のライフサイクルに基いて`Loader` を管理してくれています。画面回転等で`Activity`を再生成すると、`LoaderManager`のインスタンスも引き継がれます。

で、`Loader`をキックする（非同期処理を開始する）タイミングは、`Activity#onStart()`のタイミングなので、`Activity`が起動したら`Loader`が動くようにしたい場合、`LoaderManager`で`Loader`の初期化をするのはそれ以前にしておく必要があります。

そうすると、必然的に`LoaderManager`のインスタンスを得るための`Activity#getSupportLoaderManager()` や `Activity#getLoaderManager()` も、`Activity#onStart()` 以前に呼んでおかないとダメということになります。

## LoaderCallback#onLoadFinished() の呼ばれるタイミング

`Loader`の処理が終わって結果を通知する際に、`LoaderCallback#onLoadFinished()`が呼ばれます。

`Activity#onStart()`によって Loader が動き始めるため、`Activity`のライフサイクルが一周するとそのたびに`Loader`が動作し、`LoaderCallback#onLoadFinished()`も呼ばれることになります。
なので、例えば、別の画面に遷移してから戻ってきた時も、`Activity#onRestart()`からの`Activity#onStart()`をたどるので、`LoaderCallback#onLoadFinished()` が呼ばれます。

さらに、`LoaderManager`にはバグがあって、`Fragment`が configuration change（画面回転等）で再生成されると、`LoaderCallback#onLoadFinished()`を 2 度呼びます。

何れにしても、`LoaderCallback#onLoadFinished()`は、`Activity`のライフサイクル管理によって何度も呼ばれる可能性があるものになるため、開発者は、何度も呼ばれても良いような設計・実装をしておく必要があります。
例えば、データに更新がなければ何もしない、重複したデータを持たない構造にする、など。

## One-Shot な Loader の使い方

これまでの`AsyncTask`のような、一回限りのライフサイクルの`Loader`とすることで、不用意に`LoaderCallback#onLoadFinished()`が複数回呼ばれる事象を回避出来ます。

`Loader`がその非同期処理を終え、`LoaderCallback#onLoadFinished()`に結果が通知されたタイミングで、`Loader`を破棄すれば、`LoaderManager`はそれ以上`Loader`のライフサイクルを管理しなくなります。

ただし、One-Shot であるため、`Loader`による非同期処理を実行する度に`Loader`のインスタンスを都度生成するコストが増えます。

Configuration change を気にしなくて良いのならば、`AsyncTask`を使うほうが良いかもしれませんが、`LoaderManager`は`Activity`や `Fragment`のライフサイクルを見て、適切なタイミングになるまで`Loader`の結果をキャッシュしてくれていたりもするので、まずは`Loader` を使うというところを前提に作っていくのが良いと思います。

# `LoaderCallback` と `FragmentManager`

## `LoaderCallback#onLoadFinished()` で `FragmentTransaction` を取り扱ってはならない

よく、非同期処理中に進捗ダイアログを出し、終わったら消す、みたいなことをすると思います。

で、進捗ダイアログと言えば、`DialogFragment` を用いて実装することになると思いますが、`Loader`の処理が終わったタイミングであるところの `LoaderCallback#onLoadFinished()` の中で `DialogFragment` を操作しようとすると、例外によって怒られます。

これは、`LoaderCallback#onLoadFinished()` の中で `FragmentTransaction` を取り扱うことが出来ないからです。これは公式のドキュメントにも書いてあります。

> Called when a previously created loader has finished its load. Note that normally an application is not allowed to commit fragment transactions while in this call, since it can happen after an activity's state is saved. See FragmentManager.openTransaction() for further discussion on this.

このため、`LoaderCallback#onLoadFinished()` で `FragmentTransaction` を取り扱う場合は、`Handler` を用いて、メインスレッドの `Looper` にメッセージを投げて、メインスレッド上で `FragmentTransaction` を取り扱うようにする必要があります。

```java

public void onLoadFinished(Loader<HogeLoader> loader, Hoge result) {
    new Handler().post(new Runnalbe() {
        @Override
        public void run() {
            // ここで Fragment を操作する
        }
    });
}
```

# Loader の id

`LoaderManager`は、`Activity` ごとに異なるインスタンスとなりますので、異なる`Activity`間で異なる `Loader` の ID を割り振らなければいけないということはなく、同一の`Activity`内でユニークになるように ID を割り振るだけでよいのです。