Skip to content

Asynchronous Volley

Jeff Davidson edited this page Oct 1, 2020 · 1 revision

Warning: experimental!

The AsyncRequestQueue described in this doc is relatively new; while it passes basic tests, it is not yet known how it will perform in various production scenarios. It may have bugs or perform poorly in certain conditions, and APIs are subject to change until they are part of an official release (and perhaps even after). In contrast, Volley's RequestQueue and underlying architecture are production-ready and have been used in many production applications for years. We welcome early adoption in non-critical applications or in cases where the new stack can be rolled out gradually while comparing metrics with the existing implementation. Please discuss any usage of the new stack in our user group, and report issues or feature requests on our issue tracker.

Background

Volley's client-facing API is asynchronous; requests are queued in the RequestQueue, and listeners are invoked on the UI thread as requests complete. However, the underlying implementation is synchronous - cache and network operations are performed on fixed-size thread pools. This can create a performance bottleneck for applications which wish to make many requests in parallel, and can waste a small amount of memory for applications which are not actively making requests but never terminate their RequestQueues.

AsyncRequestQueue is an alternate implementation of RequestQueue which can generally serve as a drop-in replacement (as it extends RequestQueue) and which uses asynchronous APIs. This implementation allows for more efficient integration of asynchronous HTTP libraries like Cronet. Threads may scale up and down with usage, and even shared with other components of your application if desired.

How to use

Dependencies

First, you will need to depend on a SNAPSHOT build of Volley, as AsyncRequestQueue has not yet been released in an official release. See the FAQ for how to configure this.

You will also need to ensure that CronetEngine is compiled into your library, as Volley assumes that applications will depend on it directly if needed. See the Cronet documentation for how to depend on Cronet.

Code

The simplest way to create an AsyncRequestQueue using Cronet is:

Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap
AsyncHttpStack httpStack = new CronetHttpStack.Builder(context).build();
AsyncNetwork network = new BasicAsyncNetwork.Builder(httpStack).build();
AsyncRequestQueue requestQueue = new AsyncRequestQueue.Builder(network).setCache(cache).build();
requestQueue.start();

Since AsyncRequestQueue extends RequestQueue, once the queue is created, it can be used just like a regular RequestQueue; call add() to queue new requests.

Consult the builder classes for CronetHttpStack, BasicAsyncNetwork, and AsyncRequestQueue for more options, including providing your own CronetEngine for CronetHttpStack, or your own Executors for AsyncRequestQueue.

Caveats and limitations

While applications should not generally observe major differences between the two RequestQueue implementations, there are some caveats and limitations to be aware of with the new approach, particularly when migrating existing code:

  • Request#getTimeoutMs and RetryPolicy#getCurrentTimeout are ignored. In prior HTTP stacks, these timeouts were used to set connect and read timeouts, which are intermediate timeouts; in particular, reading a response may require multiple network reads, which implies multiple applications of the timeout. As a result, requests may timeout after a much longer time than these APIs specify, which is a common source of confusion (e.g. #362).

    Cronet does not expose APIs to set these intermediate timeouts, so there is no equivalent, and it would be inappropriate to repurpose these existing timeouts as global timeouts as this may result in significantly more aggressive timeouts in practice than with other HTTP stacks.

    Cronet is a well-tuned HTTP stack, and the default timeouts are likely reasonable in many cases. That said, we plan to expose a new set of timeout APIs which applications can use to configure global timeouts that apply to the full request flow, as well as the time to wait between retries.

  • While we have added an AsyncCache interface to permit asynchronous disk reads, we have not yet provided an implementation. Regular caches like DiskBasedCache may still be used, with reads and writes being performed on the blocking executor configured by AsyncRequestQueue. Throughput should be no worse than with the synchronous stack, which was restricted to a single cache thread. However, since DiskBasedCache serializes all reads and writes, it may pose a performance bottleneck for applications which make heavy use of the cache.

    We may resolve this by extending DiskBasedCache to support parallel operations to the extent possible, by permitting use of Cronet's disk cache and bypassing Volley's, and/or by providing a proper asynchronous implementation in the future.