It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Andoid】AsyncTaskLoaderを使ってみる

Android 3.0から導入されたLoaderについて、
参考サイトをもとに勉強していましたがいまいちよくわからず。。

Loaderを継承した、AsyncTaskLoaderの方が使い方がわかりやすそうだったので、
まずはこちらを使用してみました。

Loaderの基本クラス

Loader

非同期のデータロードを行う為の抽象クラス。

LoaderManager

Loaderインスタンスを管理するためのクラス。
LoaderManagerインスタンスは、1つ以上のLoaderインスタンスを管理する。
Activityまたは、Fragmentごとに1つしか作られない。

LoaderManager.LoaderCallbacks

Activityまたは、FragmentとLoaderManager間で、
双方向にやりとりするためのコールバックインターフェース。

AsyncTaskLoaderを使ってみる

Activity側の実装

Activity側の実装では、Loaderを管理するLoaderManagerインスタンスを初期化する必要があります。
また、LoaderManagerから状態を受け取るためのコールバックとしてLoaderManager.LoaderCallbacksインターフェースが用意されているため、Activityにこれを実装します。

LoaderManagerの初期化には、getLoaderManager().initLoader()を使用します。
initLoader()は、Activity.onStart()よりも早く呼び出す必要があります。

引数について、以下のとおりです。

引数 詳細
第一引数:
int id
Loaderの識別子。
onCreateLoader()第一引数に渡される。
第二引数:
Bundle args
Loaderへのパラメタ。
onCreateLoaderメソッドの第二引数に渡される。
Loaderがすでに生成されている場合は、この値は無視される。
第三引数:
LoaderCallback callback
Loaderの状態変化に使用される。
LoaderCallbackインターフェースを継承したクラスを指定する。

以下に、実装のサンプルを記載します。

// LoaderCallbacksのジェネリクスには、Loaderの戻り値の型を指定する(以下は、String)
public class MainActivity extends Activity implements LoaderCallbacks<String> {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // LoaderManagerの初期化
        getLoaderManager().initLoader(0, null, this);
    }
    
    @Override
    public Loader<String> onCreateLoader(int id, Bundle args) {
        // Loderを初期化する
        MyLoader loader = new MyLoader(this);
        return loader;
    }

    @Override
    public void onLoadFinished(Loader<String> loader, String data) {
        // dataでは、Loderクラスの戻り値が返される
        // Loderの終了処理
        Log.d("","onLoadFinished");
    }

    @Override
    public void onLoaderReset(Loader<String> arg0) {
        // Loderが、リセットされるときに呼ばれる。
        // ここで、もらっているdataを破棄する必要がある。
        Log.d("","onLoaderReset");
    }
}

Loaderの実装

非同期処理を行うLoaderクラスは、AsyncTaskLoaderクラスを継承して作成します。
Loaderが処理を開始するには、forceLoad()を呼び出す必要があり、
以下のサンプルでは、Loderの準備が完了した際に呼ばれるonStartLoading()で実行しています。
実際にバックグラウンドで行う処理は、loadInBackground()に記載します。

public static class MyLoader extends AsyncTaskLoader<String> {
        
    public MyLoader(Context context) {
        super(context);
    }

    @Override
    public void deliverResult(String data) {
        // Loderが処理した結果を返す。(メインスレッドで実行される)
        super.deliverResult(data);
    }

    @Override
    public String loadInBackground() {
        // Loderが実行するバックグラウンド処理
        return "abc";
    }
        
    @Override
    protected void onStartLoading() {
        // Loder側の準備ができたタイミングで呼び出される
        // UIスレッドで実装される
        forceLoad();
    }
}

注意点

AsyncTaskLoaderを使う上で幾つか注意すべき点があります。


  1. 呼び出し元のActivityがアクティブでないと、onLoadFinished()コールバックが呼ばれないため、 それを考慮した実装を行う必要があります。

  2. LoaderインスタンスonStartLoading()は、Activityがアクティブになったタイミングで呼ばれます。(何度も呼ばれる) その為、一度処理した内容が再度処理されないように意識した実装を行う必要があります。


こちらを参考にしました。

これらを考慮したサンプルを以下に記載します。

public static class MyLoader extends AsyncTaskLoader<String> {

    private String data;

    public AsyncLoader(Context context) {
        super(context);
    }

    @Override
    public void deliverResult(D data) {
        if (isReset()) {
            return;
        }

        this.data = data;
        super.deliverResult(data);
    }

    @Override
    public String loadInBackground() {
        return "abc";
    }

    @Override
    protected void onStartLoading() {
        if (data != null) {
            deliverResult(data);
        }

        if (takeContentChanged() || data == null) {
            forceLoad();
        }
    }

    @Override
    protected void onStopLoading() {
        cancelLoad();
    }

    @Override
    protected void onReset() {
        super.onReset();
        onStopLoading();
        data = null;
    }
}

参考

http://www.techdoctranslator.com/android/guide/activities/loaders http://blog.loadlimits.info/2012/09/asynctaskloaderのあるactivityに戻ってきたときに再度loadinbackgroundが呼ば/ http://ijoru.com/ijoru/?p=207