前のレッスンでは、便利なメソッド 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
をサブクラス化し、RequestQueue
を Application.onCreate()
内で設定する方法があります。ただし、この方法はおすすめしません。静的シングルトンのほうが、モジュール化をさらに進めた方法で同じ機能を提供できます。
重要なのは、RequestQueue
を Activity
コンテキストではなく 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);