RequestQueue を設定する

前のレッスンでは、便利なメソッド Volley.newRequestQueue を使用し、Volley のデフォルトの動作を活用して RequestQueue を設定する方法を紹介しました。このレッスンでは、RequestQueue を作成する詳細な手順を紹介し、カスタム動作を指定できるようにします。

このレッスンでは、RequestQueue をシングルトンとして作成して、アプリのライフタイムの間その RequestQueue が存続するようにするおすすめの方法についても説明します。

ネットワークとキャッシュを設定する

RequestQueue がその役目を果たすには、ネットワークによるリクエストの転送と、キャッシュによる処理という 2 つの機能が必要です。こうした機能については、Volley ツールボックスで次のような標準的実装が入手できます。レスポンスごとに 1 ファイル分のキャッシュとそのメモリ内のインデックスを提供する DiskBasedCache と、優先 HTTP クライアントに基づくネットワーク転送を提供する BasicNetwork です。

BasicNetwork は、Volley でのネットワーク機能のデフォルト実装です。アプリがネットワークへの接続に使用している HTTP クライアントによって BasicNetwork を初期化する必要があります。通常、この接続は HttpURLConnection です。

RequestQueue の設定に関する手順を次のスニペットで示します。

Kotlin

    // Instantiate the cache
    val cache = DiskBasedCache(cacheDir, 1024 * 1024) // 1MB cap

    // Set up the network to use HttpURLConnection as the HTTP client.
    val network = BasicNetwork(HurlStack())

    // Instantiate the RequestQueue with the cache and network. Start the queue.
    val requestQueue = RequestQueue(cache, network).apply {
        start()
    }

    val url = "http://www.example.com"

    // Formulate the request and handle the response.
    val stringRequest = StringRequest(Request.Method.GET, url,
             Response.Listener<String> { response ->
                // Do something with the response
            },
            Response.ErrorListener { error ->
                // Handle error
                textView.text = "ERROR: %s".format(error.toString())
            })

    // Add the request to the RequestQueue.
    requestQueue.add(stringRequest)

    // ...
    

Java

    RequestQueue requestQueue;

    // Instantiate the cache
    Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap

    // Set up the network to use HttpURLConnection as the HTTP client.
    Network network = new BasicNetwork(new HurlStack());

    // Instantiate the RequestQueue with the cache and network.
    requestQueue = new RequestQueue(cache, network);

    // Start the queue
    requestQueue.start();

    String url ="http://www.example.com";

    // Formulate the request and handle the response.
    StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            // Do something with the response
        }
    },
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // Handle error
        }
    });

    // Add the request to the RequestQueue.
    requestQueue.add(stringRequest);

    // ...
    

リクエストは 1 回しか必要がなく、スレッドプールは残したくない場合、必要なところで RequestQueue を作成し、レスポンスまたはエラーが返ったら、その RequestQueue に対して stop() を呼び出します。Volley.newRequestQueue() メソッドを使用しますが、このメソッドについては、単純なリクエストを送信する場合の記事で説明しています。ただし、もっと一般的なユースケースでは、次のセクションで説明するように、RequestQueue をシングルトンとして作成し、アプリのライフタイムの間稼働しておきます。

シングルトン パターンを使用する

アプリがネットワークを常時使用する場合、そのアプリのライフタイムの間存続するような RequestQueue の単一のインスタンスを設定すると、おそらく最も効率がよくなります。これはさまざまな方法で実現できます。おすすめは、RequestQueue と他の Volley 機能をカプセル化するシングルトン クラスを実装する方法です。他にも、Application をサブクラス化し、RequestQueueApplication.onCreate() 内で設定する方法があります。ただし、この方法はおすすめしません。静的シングルトンのほうが、モジュール化をさらに進めた方法で同じ機能を提供できます。

重要なのは、RequestQueueActivity コンテキストではなく Application コンテキストでインスタンス化する必要があることです。これにより、RequestQueue がアプリのライフタイムの間存続することを保証できます。アクティビティが再作成されるたびに(たとえば、ユーザーがデバイスを回転させるたびに)作り直す必要はありません。

RequestQueue 機能と ImageLoader 機能を提供するシングルトン クラスの例を以下に示します。

Kotlin

    class MySingleton constructor(context: Context) {
        companion object {
            @Volatile
            private var INSTANCE: MySingleton? = null
            fun getInstance(context: Context) =
                INSTANCE ?: synchronized(this) {
                    INSTANCE ?: MySingleton(context).also {
                        INSTANCE = it
                    }
                }
        }
        val imageLoader: ImageLoader by lazy {
            ImageLoader(requestQueue,
                    object : ImageLoader.ImageCache {
                        private val cache = LruCache<String, Bitmap>(20)
                        override fun getBitmap(url: String): Bitmap {
                            return cache.get(url)
                        }
                        override fun putBitmap(url: String, bitmap: Bitmap) {
                            cache.put(url, bitmap)
                        }
                    })
        }
        val requestQueue: RequestQueue by lazy {
            // applicationContext is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            Volley.newRequestQueue(context.applicationContext)
        }
        fun <T> addToRequestQueue(req: Request<T>) {
            requestQueue.add(req)
        }
    }
    

Java

    public class MySingleton {
        private static MySingleton instance;
        private RequestQueue requestQueue;
        private ImageLoader imageLoader;
        private static Context ctx;

        private MySingleton(Context context) {
            ctx = context;
            requestQueue = getRequestQueue();

            imageLoader = new ImageLoader(requestQueue,
                    new ImageLoader.ImageCache() {
                private final LruCache<String, Bitmap>
                        cache = new LruCache<String, Bitmap>(20);

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

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

        public static synchronized MySingleton getInstance(Context context) {
            if (instance == null) {
                instance = new MySingleton(context);
            }
            return instance;
        }

        public RequestQueue getRequestQueue() {
            if (requestQueue == null) {
                // getApplicationContext() is key, it keeps you from leaking the
                // Activity or BroadcastReceiver if someone passes one in.
                requestQueue = Volley.newRequestQueue(ctx.getApplicationContext());
            }
            return requestQueue;
        }

        public <T> void addToRequestQueue(Request<T> req) {
            getRequestQueue().add(req);
        }

        public ImageLoader getImageLoader() {
            return imageLoader;
        }
    }
    

このシングルトン クラスを使って RequestQueue 操作を行う例を以下にいくつか示します。

Kotlin

    // Get a RequestQueue
    val queue = MySingleton.getInstance(this.applicationContext).requestQueue

    // ...

    // Add a request (in this example, called stringRequest) to your RequestQueue.
    MySingleton.getInstance(this).addToRequestQueue(stringRequest)
    

Java

    // Get a RequestQueue
    RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
        getRequestQueue();

    // ...

    // Add a request (in this example, called stringRequest) to your RequestQueue.
    MySingleton.getInstance(this).addToRequestQueue(stringRequest);