AsyncTaskLoader サブクラスでのデータ監視

UIスレッドと非同期処理の繋ぎ込みに加えて、データの監視から非同期更新までをお手軽に実装できるAsyncTaskLoader クラスのデータ監視についてメモ。

本格アプリを作ろう! Androidプログラミングレシピ

本格アプリを作ろう! Androidプログラミングレシピ

  • 作者: Dave Smith,Jeff Friesen,吉川邦夫
  • 出版社/メーカー: インプレスジャパン
  • 発売日: 2011/12/22
  • メディア: 単行本(ソフトカバー)
  • 購入: 2人 クリック: 108回
  • この商品を含むブログ (7件) を見る


AsyncTaskLoader の親クラスであるLoaderクラスの解説を本家のドキュメントで確認すると、下記のように、扱うコンテンツを監視して変更が合った場合に新しい情報を適宜通達してくれると書いてある。

Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:

They are available to every Activity and Fragment.
They provide asynchronous loading of data.
They monitor the source of their data and deliver new results when the content changes.
They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.


愚かな僕はこれを読んで「なるほど、データが変更されたら自動的にLoaderが色々変更してくれるのね、便利!」と早合点してしまった。

で、さらにドキュメントを読み進めると

An abstract class that performs asynchronous loading of data. This is the base class for a loader. You would typically use CursorLoader, but you can implement your own subclass. While loaders are active they should monitor the source of their data and deliver new results when the contents change.

おおう、一番最初のLoader紹介はLoaderを定義つける性質の紹介であって、そう実装されなくてはいけないものという記述だったのね。(AsyncTaskLoader もまた然り)
ということで、どうもデータの監視するところは自分で実装しないといけないみたい。

以下、リファレンスのソースを引用。
リファレンスソースはReceiverでの通知をローダー更新の契機にしている。
AsyncTaskLoader | Android Developers

以下は新しいアプリケーションがインストールされる度に通知を受けるReceiverクラス。

/**
 * Helper class to look for interesting changes to the installed apps
 * so that the loader can be updated.
 */
public static class PackageIntentReceiver extends BroadcastReceiver {
    final AppListLoader mLoader;

    public PackageIntentReceiver(AppListLoader loader) {
        mLoader = loader;
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addDataScheme("package");
        mLoader.getContext().registerReceiver(this, filter);
        // Register for events related to sdcard installation.
        IntentFilter sdFilter = new IntentFilter();
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        mLoader.getContext().registerReceiver(this, sdFilter);
    }

    @Override public void onReceive(Context context, Intent intent) {
        // Tell the loader about the change.
        mLoader.onContentChanged();
    }
}

通知を受ける度にローダークラスの更新を実行している。

 @Override public void onReceive(Context context, Intent intent) {
        // Tell the loader about the change.
        mLoader.onContentChanged();
    }

ちなみに、AsyncTaskLoaderのonStartLoading()で上記Receiverをセットしている

 /**
     * Handles a request to start the Loader.
     */
    @Override protected void onStartLoading() {
        if (mApps != null) {
            // If we currently have a result available, deliver it
            // immediately.
            deliverResult(mApps);
        }

        // Start watching for changes in the app data.
        if (mPackageObserver == null) {
            mPackageObserver = new PackageIntentReceiver(this);
        }

        // Has something interesting in the configuration changed since we
        // last built the app list?
        boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());

        if (takeContentChanged() || mApps == null || configChange) {
            // If the data has changed since the last time it was loaded
            // or is not currently available, start a load.
            forceLoad();
        }
    }

結論:

AsyncTaskLoader サブクラスにおけるデータ監視処理は自前(Receiverとか)で行う。