OkHttp, the Android HTTP Client from Square

Valdio Veliu
Valdio Veliu
Share

Most apps need network connections to external services to access and exchange data. This is typically through REST APIs and an HTTP client in your app. OKHttp is an Android HTTP client library from Square that reduces the steps needed, and means you can spend more time on the important areas of your app.

Why OkHttp?

It’s a simple library to use, loading data faster and saving bandwidth. It removes the need for network testing, recovering from common connection problems, and on a connection failure, OkHttp can retry the request with a different route.

OkHttp supports Android 2.3 and above, which is more than 99% of the market based on current statistics of version usage.

Using OkHttp in Your App

OkHttp recently updated to version 3.0, and is currently at version 3.3.1. I mention this because OkHttp made key updates in this version and methods from previous versions are deprecated.

You can find all the examples I will cover on GitHub.

Add Dependencies

Open build.gradle(Module: app) and add the following dependency, or check the OkHttp site for the latest updates.

dependencies {
    //...
    compile 'com.squareup.okhttp3:okhttp:3.3.1'
}

In AndroidManifest.xml add the internet permission.

<uses-permission android:name="android.permission.INTERNET"/>

Networking with OkHttp

The main focus of this article consists will be on building the following network calls.

  • GET
  • POST
  • Uploading files with OkHttp
  • Monitoring request and response progress
  • Handle cookies with OkHttp

Note: I try not to write redundant code, but DRY code. I feel I’ve done my best to optimize how my code handles networking with OkHttp. This consist of structuring code into two classes. ApiCall to handle POST and GET calls and RequestBuilder to construct the request URLs and RequestBodys.

Networking

Time to create some API calls, specifically the GET and POST network requests. Create the following java class to handle each of the requests.

public class ApiCall {

    //GET network request
    public static String GET(OkHttpClient client, HttpUrl url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    //POST network request
    public static String POST(OkHttpClient client, HttpUrl url, RequestBody body) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }
}

In both the methods of the ApiCall class there is an instance of OkHttpClient, which means you must pass an instance of the client on every network call. You can construct the methods differently so that a new client is built each time, but this is not a smart solution and I recommend you read this StackOverflow answer, to prove this is a better solution.

In both the methods the HttpUrl url parameter represents the URL of the request. It’s represented as an instance of HttpUrl, but can also be a String:

public static String POST(OkHttpClient client, String url, RequestBody body) throws IOException {
    //...
}

You can apply this in both methods and it has the same result as the HttpUrl instance. An important fact to mention is that the URL String might be incorrectly structured which can lead to a MalformedURLException.

The last thing to mention is the RequestBody in the POST method. It represent a collection of parameters, in name/value pairs sent in the body of a POST request.

Building the Requests URL

Next you need to create a Java class that constructs the URL of a network request and the RequestBodys of the POST methods.

public class RequestBuilder {

    //Login request body
    public static RequestBody LoginBody(String username, String password, String token) {
        return new FormBody.Builder()
                .add("action", "login")
                .add("format", "json")
                .add("username", username)
                .add("password", password)
                .add("logintoken", token)
                .build();
    }

    public static HttpUrl buildURL() {
        return new HttpUrl.Builder()
                .scheme("https") //http
                .host("www.somehostname.com")
                .addPathSegment("pathSegment")//adds "/pathSegment" at the end of hostname
                .addQueryParameter("param1", "value1") //add query parameters to the URL
                .addEncodedQueryParameter("encodedName", "encodedValue")//add encoded query parameters to the URL
                .build();
        /**
         * The return URL:
         *  https://www.somehostname.com/pathSegment?param1=value1&encodedName=encodedValue
         */
    }

}

The RequestBuilder java class contains examples for building a RequestBody for POST calls and HttpUrl instances. The examples consist of building a RequestBody for some presumed login action and an HttpUrl, usually used on a GET requests.

Back to Activity

The steps so far are to prepare the way to handle networking from an activity and make use of the classes created. First, in an activity class create a global OkHttpClient.

public class MainActivity extends AppCompatActivity {

    private OkHttpClient client;
    //...
}

Initialize the client in the onCreate() method.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...
        client = new OkHttpClient();
    //...
}

You must handle network calls in Android asynchronously, otherwise a NetworkOnMainThreadException is thrown when an application attempts to perform a networking operation on the main thread. To avoid this exception, use AsyncTask.

Add these methods to your Activity:

private void loadContent() {
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            try {
                response = ApiCall.GET(client, RequestBuilder.buildURL());
                //Parse the response string here
                Log.d("Response", response);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }.execute();
}

private void attemptLogin(String url) {
    new AsyncTask<String, Void, Void>() {
        @Override
        protected Void doInBackground(String... params) {

            try {
                response = ApiCall.POST(
                        client,
                        params[0],
                        RequestBuilder.LoginBody("username", "password","token"));

                Log.d("Response", response);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }.execute(url);
}

The two functions shown here represent both examples in the RequestBuilder class. They are just for demonstrative purposes and you could construct them differently.

Leaving aside the implementation of the AsyncTasks, notice the code that handles networking. All the code implemented in both previous classes is structured in a single line, which return the response body of the network request and there is no redundant code. Pretty cool!

ApiCall.GET(client, RequestBuilder.buildURL());

After you have retrieved the response, some parsing might be needed, as APIs commonly return JSON or XML feeds.

Uploading Files

In this section I will cover uploading files with OkHttp. One of the common upload types required is multipart/form-data . The following image upload example is how OkHttp could handle this, but is valid for other different file types.

//Upload request body
public static MultipartBody uploadRequestBody(String title, String imageFormat, String token, File file) {

    MediaType MEDIA_TYPE = MediaType.parse("image/" + imageFormat); // e.g. "image/png"
    return new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("action", "upload")
            .addFormDataPart("format", "json")
            .addFormDataPart("filename", title + "." + imageFormat) //e.g. title.png --> imageFormat = png
            .addFormDataPart("file", "...", RequestBody.create(MEDIA_TYPE, file))
            .addFormDataPart("token", token)
            .build();
}

This function returns a MultipartBody object derived from RequestBody, so it’s a good idea to put this method in the RequestBuilder class.

Now that the body of the upload request is complete, how do you run the upload call? Uploads are POST calls to the API, so its the same as the previous calls.

 File file = new File(""); //provide a valid file
 ApiCall.POST(client, url, RequestBuilder.uploadRequestBody("title", "png", "someUploadToken", file));

Remember to call this asynchronously. This call returns the verification of the upload action, successful or not.

What’s Next

That’s all for the basics of OkHttp, but next I will mention some tips and tools that might be useful with OkHttp.

Monitoring Request Progress

Often useful when uploading media is the ability to monitor progress. Some time ago I found this Gist solution that is the solution to this problem. To monitor a request’s progress with this solution, you create an instance of RequestBody and then pass it as an argument in the constructor of the class.

//Monitored request
File file = new File("");
MultipartBody body = RequestBuilder.uploadRequestBody("title", "png", "someUploadToken", file);

CountingRequestBody monitoredRequest = new CountingRequestBody(body, new CountingRequestBody.Listener() {
    @Override
    public void onRequestProgress(long bytesWritten, long contentLength) {
        //Update a progress bar with the following percentage
        float percentage = 100f * bytesWritten / contentLength;
        if (percentage >= 0) {
            //TODO: Progress bar
        } else {
            //Something went wrong
        }
    }
});

I changed the code for use it with OkHttp 3. Here is a link to the class in GitHub with the appropriate changes .

Handle Cookies with OkHttp

You can handle cookies in OkHttp with CookieJar. The following example is an example of how to handle cookies in OkHttp.

public class CookieStore implements CookieJar {

    private final Set<Cookie> cookieStore = new HashSet<>();

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        /**
         *Saves cookies from HTTP response
         * If the response includes a trailer this method is called second time
         */
        //Save cookies to the store
        cookieStore.addAll(cookies);
    }

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        /**
         * Load cookies from the jar for an HTTP request.
         * This method returns cookies that have not yet expired
         */
        List<Cookie> validCookies = new ArrayList<>();
        for (Cookie cookie : cookieStore) {
            LogCookie(cookie);
            if (cookie.expiresAt() < System.currentTimeMillis()) {
                // invalid cookies
            } else {
                validCookies.add(cookie);
            }
        }
        return validCookies;
    }

    //Print the values of cookies - Useful for testing
    private void LogCookie(Cookie cookie) {
        System.out.println("String: " + cookie.toString());
        System.out.println("Expires: " + cookie.expiresAt());
        System.out.println("Hash: " + cookie.hashCode());
        System.out.println("Path: " + cookie.path());
        System.out.println("Domain: " + cookie.domain());
        System.out.println("Name: " + cookie.name());
        System.out.println("Value: " + cookie.value());
    }
}

To handle cookies based on the specified policies in the CookieStore class, you have to apply these policies to the OkHttpClient as follows.

OkHttpClient client = new OkHttpClient.Builder()
    .cookieJar(new CookieStore())
    .build();

Now you have updated the OkHttpClient to accept cookies, all the network requests cookies will be handled with the policies implemented in the CookieStore class.

This example is enough for session cookies, but for those who want to save cookies through subsequent app launches, this policies is not enough. There is a solution to this problem by using the PersistentCookieJar implementation based on SharedPreferences. To use this package with a OkHttpClient follow their usage guidelines.

All OK?

In this article I covered getting started with OkHttp and shared advice that might come in handy while working with it. I hope you find the library saves you time and would love to know what you create with it.

Frequently Asked Questions (FAQs) about Consuming Web APIs in Android with OkHttp

How do I set up OkHttp in my Android project?

To set up OkHttp in your Android project, you need to add the OkHttp dependency to your build.gradle file. You can do this by adding the following line to your dependencies section: implementation ‘com.squareup.okhttp3:okhttp:4.9.0’. After adding this line, sync your project with Gradle files. OkHttp is now ready to be used in your Android project.

How can I make a synchronous GET request with OkHttp?

To make a synchronous GET request with OkHttp, you need to create an instance of OkHttpClient and a Request object. The Request object specifies the URL of the server endpoint. Then, you call the execute() method on the client, passing the Request object as a parameter. This method returns a Response object, which contains the server’s response to your request.

How can I make a POST request with OkHttp?

To make a POST request with OkHttp, you need to create an instance of OkHttpClient, a RequestBody, and a Request object. The RequestBody contains the data you want to send to the server. The Request object specifies the URL of the server endpoint and the method (POST). Then, you call the newCall() method on the client, passing the Request object as a parameter. This method returns a Call object. You can then call the execute() method on the Call object to send the request and receive the server’s response.

How can I add headers to my OkHttp request?

To add headers to your OkHttp request, you can use the addHeader() method on the Request.Builder class. This method takes two parameters: the name of the header and its value. You can add multiple headers by calling this method multiple times before building the Request object.

How can I handle errors in OkHttp?

OkHttp uses exceptions to indicate problems. If a request fails, OkHttp throws an IOException. You can catch this exception and handle it in your code. If the server responds with an error status code, you can check the Response object’s isSuccessful() method. If this method returns false, you can read the error message from the ResponseBody.

How can I cancel a request in OkHttp?

To cancel a request in OkHttp, you can call the cancel() method on the Call object. This method cancels the request if it hasn’t been executed yet or interrupts the call if it’s currently running.

How can I send a multipart request with OkHttp?

To send a multipart request with OkHttp, you need to create a MultipartBody and add parts to it using the addFormDataPart() method. Each part represents a different piece of data. Then, you create a Request object, specifying the URL of the server endpoint, the method (POST), and the MultipartBody. Finally, you send the request as usual.

How can I use OkHttp with Retrofit?

Retrofit is a type-safe HTTP client for Android and Java, and it uses OkHttp as its default network layer. To use OkHttp with Retrofit, you just need to add the Retrofit and OkHttp dependencies to your build.gradle file. Then, you can create a Retrofit instance, specifying the base URL of your API and the OkHttpClient instance to use.

How can I log HTTP requests and responses with OkHttp?

OkHttp includes an HTTP logging interceptor that logs the details of HTTP requests and responses. To use it, you need to add the logging interceptor dependency to your build.gradle file, create an instance of HttpLoggingInterceptor, set its level to BODY, and add it to your OkHttpClient instance.

How can I handle cookies with OkHttp?

OkHttp includes a CookieJar interface that you can implement to handle cookies. The CookieJar has two methods: saveFromResponse() and loadForRequest(). The saveFromResponse() method is called when the server sets cookies, and the loadForRequest() method is called when preparing a request to the server. You can use these methods to save and load cookies as needed.