设置 RequestQueue

上一课介绍了如何使用便捷方法 Volley.newRequestQueue 来设置 RequestQueue,以充分利用 Volley 的默认行为。本课会详细介绍创建 RequestQueue 的明确步骤,以便您提供自己的自定义行为。

本课还会介绍将 RequestQueue 创建为单例这种推荐做法,这样做会使 RequestQueue 在您应用的整个生命周期内持续不断运行。

设置网络和缓存

RequestQueue 需要两样东西才能发挥作用:一样是网络,用于执行请求传输操作,另一样是缓存,用于处理缓存。Volley 工具箱中提供了这两样东西的标准实现:DiskBasedCache 可提供每次响应一个文件的缓存(以及内存索引),而 BasicNetwork 则可根据您的首选 HTTP 客户端提供网络传输。

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);

    // ...
    

如果您只需要发出一个一次性请求但又不想离开任何线程池,则可以在任何需要的地方创建 RequestQueue,并使用发送简单请求中介绍的 Volley.newRequestQueue() 方法,在响应或错误返回后对 RequestQueue 调用 stop()。不过,比较常见的用例是将 RequestQueue 创建为单例,以使其在您应用的整个生命周期内不断运行(如下一节所述)。

使用单例模式

如果您的应用经常使用网络,那么最有效的方式可能是设置单个 RequestQueue 实例,使其在您应用的整个生命周期内不断运行。您可以通过多种方式实现此目标。建议您实现包含 RequestQueue 和其他 Volley 功能的单例类。另一种方法是子类化 Application 并在 Application.onCreate() 中设置 RequestQueue。但我们不建议采用这种方法;静态单例能够以更加模块化的方式提供相同的功能。

这里有一个很关键的点是,您必须使用 Application 上下文(而不是 Activity 上下文)对 RequestQueue 进行实例化。这样可确保 RequestQueue 在您应用的整个生命周期内不断运行,而不是在每次重新创建 Activity(例如,当用户旋转设备时)后都重新创建。

以下示例展示了提供 RequestQueueImageLoader 功能的单例类:

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);