Cronet - Chromium Network Stack for Android
Cronet is a Chromium network stack used internally by Google inside its mobile apps for reliable network requests. Recently, Google made the library available for Android, IOS but the network stack has been part of Chrome web browser for a long time.
Using Cronet, the requests can be given priorities and responses can be cached in memory or on the disk. Once set the priorities, the server can decide the order to execute the requests and once cached, the future requests are fetched automatically from the cache.
Cronet requests are asynchronous which means it doesn't even block the worker threads. This network stack provides support for HTTP, HTTP2, and QUIC (Quick UDP Internet Connections) protocols. It uses Brotli Compressed Data Format to perform lossless compression.
Cronet automatically sets the request type to GET or POST based on the presence of the request body or the developer can always manually set the request type to their preferred choice.
Let's Create a Simple Request using Cronet.
1) Add the dependency to module level
build.gradle
implementation 'org.chromium.net:cronet-embedded:71.3578.98'
2) Build the Cronet engine using the builder. One can use the builder the set the storage paths or enable QUIC support or Brotli compression or more.
// Build a Cronet engine val cronetEngine = CronetEngine.Builder(this) .build()
3) Build a request by passing an url to
newUrlRequestBuilder()
provided by the Cronet Engine and start the connection.// Build the request val request = cronetEngine.newUrlRequestBuilder( "https://jsonplaceholder.typicode.com/todos/1", RequestCallback(), Executors.newSingleThreadExecutor() ).build() // Start the request request.start()
Once, the connection starts, the request lifecycle kicks in. The lifecycles trigger different callbacks which are used to handle the response and the errors. So, let's implement the necessary callbacks.
4) Implement a callback that extends
4) Implement a callback that extends
UrlRequest.Callback()
and override methods to receive the response.override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) { Log.i(TAG, "Response Started") val statusCode = info?.httpStatusCode Log.i(TAG, "Status Code $statusCode") if (statusCode == 200) { // Read the buffer request?.read(ByteBuffer.allocateDirect(32 * 1024)) } } override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) { Log.i(TAG, "Response Completed") // Flip the buffer byteBuffer?.flip() // Convert the byte buffer to a string byteBuffer?.let { val byteArray = ByteArray(it.remaining()) it.get(byteArray) String(byteArray, Charset.forName("UTF-8")) }.apply { Log.d(TAG, "Response: $this") } // Clear the buffer byteBuffer?.clear() // Read the buffer request?.read(byteBuffer) } override fun onFailed(request: UrlRequest?, info: UrlResponseInfo?, error: CronetException?) { Log.e(TAG, "Response Failed: ${error?.message}") } override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) { Log.i(TAG, "Response Succeeded") } override fun onRedirectReceived(request: UrlRequest?, info: UrlResponseInfo?, newLocationUrl: String?) { Log.i(TAG, "Response Redirect to $newLocationUrl") request?.followRedirect() } override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) { super.onCanceled(request, info) Log.i(TAG, "Response cancelled") }
Cronet Request Lifecycle:
As previously mentioned, the Cronet lifecycle triggers different callbacks and needs to handled by the developer. In the above discussed simple request, the following callbacks are implemented and triggered at various cycles.If the provided endpoint contains re-directs,
onRedirectReceived
callback is triggered. You can follow the re-direct or cancel
the request.Once all the headers are received and all redirects are followed,
onResponseStarted
callback is triggered.Unlike retrofit or Volley, Cronet returns the responses to the same callback for 2xx,4xx,5xx response codes. So, the developer needs to check the response code before parsing the response.
Irrespective of the response codes, you still need to call the
read
method so that the lifecycle can continue its journey and trigger the next callback.
Once onResponseStarted is read,
This will trigger
The below picture summarises the entire lifecycle of a Cronet request.
onReadCompleted
is triggered where you can read the response as byte buffer. In the above example, byte buffers are converted to bytes then to string and printed to logcat. To continue the journey in the lifecycle, call read method. This will trigger
onSucceeded
callback as the Cronet request got successfully completed.
onFailed
is called if the Cronet request fails abruptly and onCanceled
is triggered if the request is cancelled by calling the cancel
method. If so, simply, free the resources.The below picture summarises the entire lifecycle of a Cronet request.
If you are having trouble understanding the article or want to explore the code yourself, feel free to fork the Cronet Example project on Github.