Volley を Android アプリで使うときのオレオレプラクティス

### TL;DR

`Application` クラスを継承した `MyApplication` クラスを定義して、シングルトンパターンでオブジェクトを取得できるようにする。 `MyApplication` オブジェクト経由で `RequestQueue` オブジェクトへのアクセスを行うようにする。

### 概要

Volley は 2013 年の Google I/O で発表されたライブラリで、シンプルながらも強力な API を備えた便利なライブラリです。この Volley ですが、いざ使おうと思って調べてみると **RequestQueue はシングルトン的に使う方が良い** というような情報がいくつか出てきますが、実際どうやってシングルトン的に扱うのかが最初判りませんでした。

色々やった結果、 `Application` クラスを継承したクラスを作ってあげて、アプリケーションの起動時にオブジェクトを生成し、`Application` 経由で `RequestQueue` を取得するようにするとうまくいきました。

### MyApplication.java

```java
package net.imthinker.android.volley;

import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

public class MyApplication extends Application {
    private static MyApplication MY_APPLICATION;
    private static Context sContext;

    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;

    public static MyApplication getInstance() {
        if (MY_APPLICATION == null) {
            MY_APPLICATION = new MyApplication();
            MY_APPLICATION.setRequestQueue();
        }
        return MY_APPLICATION;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sContext = getApplicationContext();
        MY_APPLICATION = new MyApplication();
        MY_APPLICATION.setRequestQueue();
    }

    /**
     * Application Context を取得する
     *
     * @return Context オブジェクト
     */
    public Context getContext() {
        if (sContext == null) {
            sContext = getApplicationContext();
        }
        return sContext;
    }

    /**
     * RequestQueue を取得する
     *
     * @return RequestQueue オブジェクト
     */
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            setRequestQueue();
        }
        return mRequestQueue;
    }

    /**
     * ImageLoader を取得する
     *
     * @return ImageLoader オブジェクト
     */
    public ImageLoader getImageLoader() {
        if (mRequestQueue == null) {
            setRequestQueue();
        } else if (mImageLoader == null) {
            mImageLoader = new ImageLoader(mRequestQueue, new ImageLruCache());
        }
        return mImageLoader;
    }

    /**
     * Volley の RequestQueue と ImageLoader を初期化する
     */
    private void setRequestQueue() {
        mRequestQueue = Volley.newRequestQueue(getContext());
        mImageLoader = new ImageLoader(mRequestQueue, new ImageLruCache());
    }

    /**
     * 画像のキャッシュを行う LruCache
     * ※ 参考: http://qiita.com/gari_jp/items/829a54bfa937f4733e29
     */
    public static class ImageLruCache implements ImageLoader.ImageCache {
        private LruCache<String, Bitmap> mMemoryCache;

        public ImageLruCache() {
            int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
            int cacheSize = maxMemory / 8;

            mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getByteCount() / 1024;
                }
            };
        }

        @Override
        public Bitmap getBitmap(String url) {
            return mMemoryCache.get(url);
        }

        @Override
        public void putBitmap(String url, Bitmap bitmap) {
            mMemoryCache.put(url, bitmap);
        }
    }
}
```

### AndroidManifest.xml (抜粋)

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.imthinker.android.volley">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
```

あとは `MyApplication.getInstance().getRequestQueue();` でリクエストキューを取得できるので、 Volley を使った通信が必要な場面で呼び出して、キューに `Request` オブジェクトを詰め込んで行く感じです。 `ImageLoader` に関しても同様です。 `ListView` の上にネットワークから取得した画像を載せる場合、 `ImageLoader` を使ってあげると Volley が上手にキャッシュしながら非同期で読み込んでくれるので便利です。

### 修正

- 2014/07/27 17:27 頂いたコメントを元に修正 & タイポを修正
- 2014/07/28 11:06 嘘つきになっていた箇所を修正
- 2015/02/24 12:09 全体的に見直し