Discover Cronet

Cronet is a network library for Android and iOS Applications. Cronet is the Chromium Network stack packaged as a library: this means this stack is used every day by Chrome.

Adopting Cronet can improve network performance of your app by reducing latency and increasing throughput.

This article introduces the Cronet library and its usage. You can find an example of how to use Cronet here. This sample shows how to load a bunch of images, stored on Google cloud, using Cronet.

Networking today

It looks like everyone uses some variation of OkHttp. You can use it through the Android framework, OkHttp directly, or Volley (configured to use either the framework or OkHttp).

The framework version of the API, HttpUrlConnection, provides a synchronous API that runs on a single thread.

OkHttp provides an asynchronous API, streams, and HTTP/2.

Volley offers request prioritization, multiple concurrent network requests, and request cancellation. Volley holds on responses in memory during parsing, so it’s more deal for small HTTP requests.

The Cronet API, on the other hand, allows asynchronous requests.

Chromium Network stack

Chromium network stack offers several advantages to improve page loading time.

Every time a host sets up a connection, there are various activities that have to be performed, like DNS resolving and handshaking. The Chromium stack addresses this using the Socket Late Binding mechanism.

Modern pages require a lot of resources and resource prioritization is a difficult problem for browsers. The Chromium stack uses Resource Prioritization where all the requests are sent to the server tagged with priorities and let the server respond in the appropriately prioritized order.

Chromium also offer a disk cache for caching web resources.

Other Cronet features

When it comes to read and write data, Cronet uses JAVA NIO ByteBuffers which offers better performances for I/O features.

As Chromium Network Stack, Cronet also allows to set priorities on requests. See the Priorities section for a code sample.

HTTP/2 and QUIC support

One of the advantages of using Cronet is HTTP/2 and QUIC support. If you are familiar with those two protocols, feel free to skip the following sections and go to Usage.

HTTP/2

HTTP/2 addresses many of the current disadvantages of HTTP by evolving the standard. As opposed to its predecessor, HTTP/2:

  • is binary, instead of textual
  • is fully multiplexed, instead of ordered and blocking: this allows doing parallel requests using one connection
  • uses header compression to reduce overhead
  • allows servers to “push” responses proactively into client caches

QUIC

The QUIC protocol (Quick UDP Internet Connections) was announced in 2012 by Google aiming to replace HTTP/2 using UDP in place of TCP.

QUIC allows the creation of connections with much less latency and is a multiplexing protocol without head-of-line blocking. This means that it solves packet loss to only block individual streams instead of all of them.

QUIC is used for every major Google Site on Desktop and Android Chrome, together with many Android apps.

Performance tests have reported pages loading 5% time faster and 1 second faster for web search at 99th-percentile. YouTube is one of the places where QUIC is used, it reported an improvement of Quality of Experience by reducing by 30% the number of rebuffers (video pauses).

Usage

CronetEngine

The first step is to instantiate a CronetEngine, as you can find in ViewAdapter.java. Normally, you would only need one CronetEngine instance, so in the sample it’s a static variable created by the method getCronetEngine(Context context).

When creating a CronetEngine, via the CronetEngine.Builder, various configuration options are available:

  1. Control the cache (pick one)
  • Use a 100KB in-memory cache by using:
engineBuilder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024)
  • Or use a 1MB disk cache by using:
engineBuilder.setStoragePath(storagePathString);
engineBuilder.enableHttpCache(CronetEngine.Builder.HttpCache.DISK,
1024 * 1024);

2. Enable HTTP2 by using:

 engineBuilder.enableHttp2(true)

3. Enable QUIC by using:

engineBuilder.enableQuic(true)

4. If you know that the server is using QUIC, you can reduce handshake time by using

engineBuilder.addQuicHint(...)

In fact, until the first response is received, Cronet doesn’t know that a server speaks QUIC and doesn’t try to use it at first. Using the addQuicHint(…) method removes the need of the discovery if the server supports QUIC or not. Moreover, by using

cronetBuilder.setStoragePath(...)
cronetBuilder.enableHttpCache(
CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 1024 * 1024)

this information will be persisted across sessions and used from the first request. Using disk cache will also persist server crypto info, for QUIC 0-rtt session establishment, in fact 0-rtt uses QUIC server info from previously negotiated session.

UrlRequest

On Android, Cronet offers its own Java asynchronous API. As you can see from the ViewAdapter class, first you will need to extend UrlRequest.Callback to handle events during the lifetime of a request.

You have 4 different callback methods called by the executor once the request has completely and partially finished:

onRedirectReceived(...)
onResponseStarted(...)
onReadCompleted(...)
onSucceeded(...)

Once you have implemented the request callback, you can then make a Request by using a UrlRequestBuilder which will combine the URL, the callback, the executor and the Cronet Engine:

// Create an executor to execute the request
Executor executor = Executors.newSingleThreadExecutor();
UrlRequest.Callback callback = new SimpleUrlRequestCallback(holder.imageViewCronet);
UrlRequest.Builder builder = new UrlRequest
.Builder(ImageRepository.getImage(position), callback, executor, cronetEngine);
// Start the request
builder.build().start();

Priorities

A request could have 5 type of priorities:

  • REQUEST_PRIORITY_IDLE
  • REQUEST_PRIORITY_LOWEST
  • REQUEST_PRIORITY_LOW
  • REQUEST_PRIORITY_MEDIUM
  • REQUEST_PRIORITY_HIGHEST

You can set the priority of the Request by using the method setPriority of the builder:

builder.setPriority(UrlRequest.Builder.REQUEST_PRIORITY_HIGHEST);

HTTPURLConnection

If you are relying on java.net.HttpURLConnection API, you can use the Cronet implementation by using:

HttpURLConnection connection = (HttpURLConnection)engine.openConnection(url);
URL.setURLStreamHandlerFactory(engine.createURLStreamHandlerFactory());

Please note that Cronet’s HttpURLConnection implementation has some limitations with regards to the system implementation. For instance, it does not use the default system HTTP cache. Async API could be more performant than HttpURLConnection given some synchronization is done to convert the async non-blocking API to a sync blocking API.

Debugging

You can use NetLog to get more information about how Cronet is processing network requests.

In order to do that, you need to instrument the code to start and stop NetLog by calling CronetEngine.startNetLogToFile and CronetEngine.stopNetLog. Have a look at the method called startNetLog() in the sample, .

Log files can be read via navigating to chrome://net-internals#import using a Chrome browser.

This is how NetLog will look like:

Please note that turning on NetLog tracking will affect the network performance.

Library

As described on the README file, getting the latest version of the library is a manual process today.

Size of the library is between 3–5MB depending on architecture.

Conclusion

Cronet is an open source library to make HTTP, HTTP/2 and QUIC calls on Android and IOS. Try it today on your app to boost your app network performance.

Sources

https://www.chromium.org/developers/design-documents/network-stack

https://www.chromium.org/developers/design-documents/network-stack/network-stack-use-in-chromium

Thanks to Jeff Kingyens.