Most apps need network connections to external services to access and exchange data. OKHttp is an Android HTTP client library from Square that reduces the steps needed.
OKHttp removes the need for network testing, recovering from common connection problems, and on a connection failure, it can retry the request with a different route.
OKHttp is built on top of the Okio library, which tries to be more efficient about reading and writing data than the standard Java I/O libraries by creating a shared memory pool. It also is the underlying library for Retrofit library that provides type safety for consuming REST-based APIs.
Also OKHttp supports both synchronous blocking calls and async calls with callbacks.
Let's add dependencies. Open build.gradle and add the following dependency, or check the OKHttp site for the latest updates.
dependencies { //... compile 'com.squareup.okhttp3:okhttp:3.8.1' }
Makes sure to enable the use of the Internet permission in your AndroidManifest.xml file
Creating request objects for make network calls
To use OkHttp you need to create a Request
object.
Request request = new Request.Builder() .url("http://www.server.com/file.txt") .build();
You can also add parameters
HttpUrl.Builder urlBuilder = HttpUrl.parse("http://www.server.com/file.txt").newBuilder(); urlBuilder.addQueryParameter("agent", "curl"); String url = urlBuilder.build().toString(); Request request = new Request.Builder() .url(url) .build();
If there are any authenticated query parameters, headers can be added to the request too
Request request = new Request.Builder() .header("Authorization", "TOKEN") .url("http://www.server.com/admin") .build();
Sending and receiving network calls
To make a synchronous network call, use the Client
to create a Call
object and use the execute
method.
OkHttpClient client = new OkHttpClient(); public void onButtonClick(View v) { final Request request = new Request.Builder() .url("http://www.server.com/file.txt") .build(); new Thread(new Runnable() { @Override public void run() { try { Response response = client.newCall(request).execute(); Log.d(TAG, response.body().string()); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
To make asynchronous calls, also create a Call
object but use the enqueue
method, and passing an anonymous callback object that implements both onFailure()
and onResponse()
.
OkHttp creates a new worker thread to dispatch the network request and uses the same thread to handle the response. If you need to update any views, you will need to use runOnUiThread()
or post the result back on the main thread.
client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, final Response response) throws IOException { if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } else { final String responseData = response.body().string(); Log.d(TAG, responseData); // Run view-related code back on the main thread MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { try { TextView tv = (TextView) findViewById(R.id.myTextView); tv.setText(responseData); } catch (IOException e) { e.printStackTrace(); } } } } } }
Assuming the request is not cancelled and there are no connectivity issues, the onResponse()
method will be fired. It passes a Response
object that can be used to check the status code, the response body
, and any headers
that were returned. Calling isSuccessful()
for instance if the code returned a status code of 2XX (i.e. 200, 201, etc.)
if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); }
The header responses are also provided as a list:
Headers headers = response.headers(); for (int i = 0; i < headers.size(); i++) { Log.d(TAG, headers.name(i) + ": " + headers.value(i)); }
The headers
can also be access directly using response.header()
:
String header = response.header("Date");
We can also decode the JSON-based data by using Gson.
Caching network responses
We can setup network caching by passing in a cache when building the OkHttpClient
:
int cacheSize = 10 * 1024 * 1024; // 10 MiB Cache cache = new Cache(new File(getApplication().getCacheDir(), "cacheFileName"), cacheSize); OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
We can control whether to retrieve a cached response by setting the cacheControl
property on the request. For instance, if we wish to only retrieve the request if data is cached, we could construct the Request
object as follows:
Request request = new Request.Builder() .url("http://www.server.com/file.txt") .cacheControl(new CacheControl.Builder().onlyIfCached().build()) .build();
We can also force a network response by using noCache()
for the request:
.cacheControl(new CacheControl.Builder().noCache().build())
We can also specify a maximum staleness age for the cached response:
.cacheControl(new CacheControl.Builder().maxStale(365, TimeUnit.DAYS).build())
To retrieve the cached response, we can simply call cacheResponse()
on the Response
object:
Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) {} @Override public void onResponse(Call call, final Response response) throws IOException { final Response text = response.cacheResponse(); // if no cached object, result will be null if (text != null) { Log.d(TAG, text.toString()); } } });
POST request to server
We can send POST request to server using following approach
@Override public void onClick(View v) { String user = edUser.getText().toString(); String password = edPassword.getText().toString(); PostHandler handler = new PostHandler(user, password); String result = null; try { result = handler.execute(URL).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } Log.d(TAG, result); } public class PostHandler extends AsyncTask<String, Void, String> { OkHttpClient client = new OkHttpClient(); String user, password; public PostHandler(String user, String password) { this.user = user; this.password = password; } @Override protected String doInBackground(String... params) { RequestBody formBody = new FormEncodingBuilder() .add("user", user) .add("password", password) .build(); Request request = new Request.Builder() .url(params[0]).post(formBody) .build(); try { Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response.toString()); return response.body().string(); } catch (Exception e) {} return null; } }
Send JSON to server
private String post(String url, String data) throws IOException { MediaType JSON = MediaType.parse("application/json; charset=utf-8"); RequestBody body = RequestBody.create(JSON, data); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string(); }