Volley で画像キャッシュ

Android 用の Volley なるライブラリの存在を知った。

調べてみるといろいろと便利そうだ。特に、画像を HTTP でダウンロードしてくるところなんて超簡単になりそう。早速フォーミュラニュースまとめ読みのフォトギャラリーに使ってみることにした。その時のメモ。

(1) ディスクキャッシュのサイズを指定して RequestQueue を作成

「Android Volley」でググって出てくるサンプルは大体以下のように RequestQueue を作成している。


mRequestQueue = Volley.newRequestQueue(getApplicationContext());

Volley.java と DiskBasedCache.java を見ればわかるが... これだと 5MB しかディスクキャッシュを使わない。画像キャッシュの場合どうなの?もう少し多くてもいいんじゃない?

と、なるとまず Volley.java の newRequestQueue() メソッドは使わない。newRequestQueue() メソッドのソースを参考に、以下のようなメソッドを作成すればいい。


public static RequestQueue newRequestQueue(Context context, int cacheSize, HttpStack stack) {
    File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

    String userAgent = "volley/0";
    try {
        String packageName = context.getPackageName();
        PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
        userAgent = packageName + "/" + info.versionCode;
    } catch (NameNotFoundException e) {}

    if (stack == null) {
        if (Build.VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {
            // Prior to Gingerbread, HttpUrlConnection was unreliable.
            // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
    }

    Network network = new BasicNetwork(stack);

    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir, cacheSize), network);
    queue.start();

    return queue;
}

public static RequestQueue newRequestQueue(Context context, int cacheSize) {
    return HttpUtils.newRequestQueue(context, cacheSize, null);
}

ポイントは new DiskBasedCache(cacheDir, cacheSize)。DiskBasedCache のインスタンスを作成するとき、元のソースは new DiskBasedCache(cacheDir) となっている。DiskBasedCache のソースを見ればわかるが、このコンストラクタはディスクキャッシュサイズにデフォルトの値を使用する。ディスクキャッシュサイズを指定可能なコンストラクタを使って DiskBasedCache クラスのインスタンスを作ってあげればいいだけ。

ってことで、今回作った newRequestQueue を使って RequestQueue のインスタンスを生成すれば、ディスクキャッシュサイズなんて思いのまま。


// キャッシュサイズに 32MB を指定
mRequestQueue = newRequestQueue(getApplicationContext(), 32 * 1024 * 1024);

(2) 画像の取得に成功/失敗したときに独自のコードを実行したい

さて、実際に Volley で画像を取得してみるとする。ググると出てくるサンプルには NetworkImageView を使う例と ImageLoader を使う例がある。

GridView なんかに小さいサムネイル画像を読み込む... なんて場合は NetworkImageView をそのまま使ってもいいかも知れない。GridView の ArrayAdapter で convertView が使いまわされたときの対処もこれだと簡単だ。内部でちゃんと cancelRequest() を呼んでくれている。

だがしかし、NetworkImageView は ImageView の拡張だ。TouchImageView とか、独自の ImageView に画像をロードしたい場合は素直に ImageLoader を使おう。これでも AsyncTask 使うよりずっと楽だ。


ImageListener listener = ImageLoader.getImageListener(
    touchImageView, 
    android.R.drawable.ic_menu_rotate, 
    android.R.drawable.ic_delete);
mImageLoader.get("http://hogehoge/image.jpg", listener);

こんなサンプルをググると発見することができる。ImageListener を ImageLoader の getImageListener() メソッドで作成している。当たり前だが、これだと画像のロード後に自前のコードを実行するとかできない。具体的にはロード後に ProgressBar を非表示にしたい... とか。

そんなときは ImageListener を自前で実装すればよい。ImageLoader.getImageListener() のソースを見てみればいい。実に簡単なコードだということが分かる。これくらいなら処理ごとに getImageListener() を自前で作ったってそんなコストにはならない。


public static ImageLoader.ImageListener getImageListener(final ImageView view, 
    final ProgressBar progress, final int defaultImageResId) {
    return new ImageListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            if (defaultImageResId != 0) {
                view.setImageResource(defaultImageResId);
            }
            view.setVisibility(View.VISIBLE);
            progress.setVisibility(View.GONE);
        }

        @Override
        public void onResponse(ImageContainer response, boolean isImmediate) {
            if (response.getBitmap() != null) {
                view.setImageBitmap(response.getBitmap());
                view.setVisibility(View.VISIBLE);
                progress.setVisibility(View.GONE);
            }
        }
    };
}

とかね。ポイントは onResponse() だと思う。この onResponse() は、キューの実行中に何度か呼ばれる。なので、response.getBitmap() が != null だったときだけ、ProgressBar を非表示にしてあげればいい。

「response.getBitmap() がずっと null だったら ProgressBar が表示されたままになるじゃん!」 ってことに関しては問題にしなくてもいい。以下の場合は onErrorResponse() が呼ばれるのを確認している。

  • requestUrl のホストが見つからない。
  • requestUrl が 404 not found.
  • requestUrl が画像じゃない。

...こんな感じで。

  • Date:July 9, 2013
  • Writer:koji27
  • Categories:Android, Java

Recent articles

Categories

Monthly archive

  • (Empty)