Android
AsyncTaskLoader

AsyncTaskLoaderとImageViewで非同期読み込みしよう

More than 1 year has passed since last update.

Android3.0からAsyncTaskの代わりにAsyncTaskLoaderという機能が付きました(AsyncTaskは非推奨になりましたし)。なんかWebの情報みるとすごく使いにくそうな感じ書いてあるので、多分よく使う画像の非同期読み込みについてこちらにまとめたいと思いました。
流れとしては、非同期で動くワーカーの作成、ワーカーを実行・結果を受け取るImageViewの作成、ローダーを動かす処理になります。

ワーカーを作成

非同期で動作するクラスを作ります。今回は非同期で画像を取得し、Bitmapを返すワーカーを作ります。

public class AsyncWorker extends AsyncTaskLoader<Bitmap> {

    private Bitmap bitmap;
    private String url

    public AsyncWorker(Context context, String url) {
        super(context);
        this.url = url;
    }

    @Override
    public Bitmap loadInBackground() {
        Bitmap bmp = null;

        //url から画像を取得する処理・・・

        return bmp;
    }
}

非同期で動作させるにはAsyncTaskLoaderを継承する必要があります。また、結果として返すクラスも指定する必要があります。今回はBitmapを返すので AsyncTaskLoader を継承します。
必要なのはsuperにコンテキストを渡すコンストラクタと非同期処理を行うloadInBackground()です。

loadInBackground

UIスレッドとは別のスレッドで実行されるメソッド

シンプルですが、これで非同期で動くワーカーが出来ました。

ワーカーからBitmapを受け取るカスタムImageViewを作成

AsyncWorkerを作成、実行、結果を受け取るクラスを作ります。今回はBitmapを受け取ってセットするのでカスタムImageViewを作成します。

public class AsyncLoaderImageView extends ImageView implements
        LoaderCallbacks<Bitmap> {

    private String url;

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

    public AsyncLoaderImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AsyncLoaderImageView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setImageUrl(String url) {
        this.url = url;
    }

    @Override
    public Loader<Bitmap> onCreateLoader(int i, Bundle bundle) {

        return new AsyncWorker(getContext(), url);
    }

    @Override
    public void onLoadFinished(Loader<Bitmap> loader, Bitmap bm) {
        setImageBitmap(bm);
    }

    @Override
    public void onLoaderReset(Loader<Bitmap> arg0) {
    }
}

ImageViewを継承したAsyncLoaderImageViewを作成します。ワーカーから結果を受け取るためにはLoaderCallbacksを実装します。また、ワーカーと同様に受け取るクラスも指定する必要があります。今回はBitmapなのでLoaderCallbacksを実装します。urlから画像を取得するので、setImageUrlを作成しています。

public Loader onCreateLoader(int i, Bundle bundle)

ローダーマネージャからローダーが呼び出される時に最初に呼ばれます。ここでワーカーの作成など、非同期処理の初期化を行います。

public void onLoadFinished(Loader loader, Bitmap bm)

ワーカーの終了した時に呼ばれます。loadInBackgroundの返り値がbmに入ってきます。今回はImageViewがにセットするのでsetImageBitmap(bm)を実行します。

public void onLoaderReset(Loader arg0)

ローダーがリセットされた際に呼ばれます。

これでワーカーが実行されるとImageViewにBitmapがセットされるまでが出来ました。
次に、ローダーを実行する処理を書きます。

ローダーを実行

ローダーを実行するにはLoaderManagerが必要です。LoadeManagerはFragmentActivityかFragmentから取得できます。

FragmentActivityのローダーを使う(supportパッケージ)

public class CustomActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String url = "http://url";
        AsyncLoaderImageView imageView = (AsyncLoaderImageView)findViewById(R.id.imageView);
        imageView.setUrl(url);
        int id = 0;
        getSupportLoaderManager().initLoader(id, null, imageView);//ローダーを実行
    }
}

Fragmentのローダーを使う

public class CustomFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment, container, false);
                String url = "http://url";
        AsyncLoaderImageView imageView = (AsyncLoaderImageView)findViewById(R.id.imageView);
        imageView.setUrl(url);
        int id = 0;
        getLoaderManager().initLoader(id, null, imageView);//ローダーを実行
        return view;
    }
}

まとめ

これで非同期で画像を読み込むことができます。この使い方は結構シンプルな使い方ですが、AsyncTaskに比べシンプルになり使いやすくなった印象です。ただ、AsyncTaskのonPreExecute()のように進捗を知ることができないので、そういうものに使うことができないというところを抑えておけば大丈夫だと思います。

注意

これはとても単純な例です。このままListViewなどViewの再利用をする処理では問題があります。
次回にAsycLoaderImageViewをListViewなどで使いまわす場合の処理について書いていこうと思います。