time

How to do GET and POST requests in Android using OkHttp

android_okhttp.png 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();
}
comments powered by Disqus