OkHttp is an HTTP client from Square for Java and Android applications. It’s designed to load resources faster and save bandwidth. OkHttp is widely used in open-source projects and is the backbone of libraries like Retrofit, Picasso, and many others.
Here are the key advantages to using OkHttp:
In this guide, we’ll cover the basics of OkHttp by building an imaginary to-do list application for Android.
First, let’s define some functional requirements for our to-do list app. Our users will want to be able to see their saved to-dos from the to-do server, save a new to-do on the server, and securely and solely access their own to-dos.
As developers, we want to be able to easily debug the network communication of our app and reduce the load on the server side.
The stable OkHttp 4.x works on Android 5.0+ (API level 21+) and Java 8+. If you require lower Android and Java version support, you can still rely on OkHttp 3.12.x branch with some considerations.
When importing OkHttp, it will also bring two dependencies: Okio, a high-performance I/O library, and the Kotlin Standard library. You don’t have to import these separately.
To use OkHttp in your Android project, you need to import it in the application-level Gradle file:
implementation("com.squareup.okhttp3:okhttp:4.9.1")
Don’t forget that on Android, you need to request the INTERNET
permission in the AndroidManifest.xml
file of your application if you would like to access network resources:
<uses-permission android:name="android.permission.INTERNET"/>
In order for our users to see all of their saved to-dos from the server, we’ll need synchronous and asynchronous GET requests, as well as query parameters.
To get our to-do list from the server, we need to execute a GET HTTP request. OkHttp provides a nice API via Request.Builder
to build requests.
Making a GET request is as easy as this:
OkHttpClient client = new OkHttpClient(); Request getRequest = new Request.Builder() .url("https://mytodoserver.com/todolist") .build(); try { Response response = client.newCall(getRequest).execute(); System.out.println(response.body().string()); } catch (IOException e) { e.printStackTrace(); }
As you can see, this is a synchronous way to execute the request with OkHttp. (You should run this on a non-UI thread, otherwise, you will have performance issues within your application and Android will throw an error.)
The asynchronous version of this request provides you with a callback when the response was fetched or an error occurred.
OkHttpClient client = new OkHttpClient(); Request getRequest = new Request.Builder() .url("https://mytodoserver.com/todolist") .build(); client.newCall(getRequest).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { e.printStackTrace(); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { System.out.println(response.body().string()); } });
Note: From now on, I will only show the synchronous version of the calls to avoid using tons of boilerplate code. I also try to use standard Java APIs whenever it is possible to make the code reusable in non-Android environments.
You can pass query parameters to your request, such as implementing filtering on the server-side for completed or incomplete to-dos.
OkHttpClient client = new OkHttpClient(); HttpUrl.Builder queryUrlBuilder = HttpUrl.get("https://mytodoserver.com/todolist").newBuilder(); queryUrlBuilder.addQueryParameter("filter", "done"); Request request = new Request.Builder() .url(queryUrlBuilder.build()) .build(); try { Response response = client.newCall(request).execute(); System.out.println(response.body().string()); } catch (IOException e) { e.printStackTrace(); }
HttpUrl.Builder
will generate the proper URL with query parameter: https://mytodoserver.com/todolist?filter=done
.
You may rightfully ask, “Why not just use the manually created URL itself?” You could. But once your URL building logic gets more complicated (more query parameters), then this class comes in handy. The developers of the library have additional reasons to use HttpUrl.
Now we have all the to-dos downloaded from our server. But how can create new to-dos or mark one as done? With a simple POST request.
Let’s send POST requests to our endpoint:
OkHttpClient client = new OkHttpClient(); RequestBody requestBody = new FormBody.Builder() .add("new", "This is my new TODO") .build(); Request postRequest = new Request.Builder() .url("https://mytodoserver.com/new") .post(requestBody) .build(); try { Response response = client.newCall(postRequest).execute(); System.out.println(response.body().string()); } catch (IOException e) { e.printStackTrace(); }
As you can see, the body of the POST request is an application/x-www-form-urlencoded
key-value pair data. But we can send any type we want. Here is an example with a JSON body:
OkHttpClient client = new OkHttpClient(); JSONObject jsonObject = new JSONObject(); jsonObject.put("todo_id", 123); jsonObject.put("status", "done"); RequestBody requestJsonBody = RequestBody.create( jsonObject.toString(), MediaType.parse("application/json") ); Request postRequest = new Request.Builder() .url("https://mytodoserver.com/modify") .post(requestJsonBody) .build(); try { Response response = client.newCall(postRequest).execute(); System.out.println(response.body().string()); } catch (IOException e) { e.printStackTrace(); }
It’s also possible that we would like to attach a file (such as an image) to our new to-do:
OkHttpClient client = new OkHttpClient(); RequestBody requestBody = new MultipartBody.Builder() .addFormDataPart("new", "This is my new TODO") .addFormDataPart("image", "attachment.png", RequestBody.create(new File("path/of/attachment.png"), MediaType.parse("image/png")) ) .setType(MultipartBody.FORM) .build(); Request postRequest = new Request.Builder() .url("https://mytodoserver.com/new") .post(requestBody) .build(); try { Response response = client.newCall(postRequest).execute(); System.out.println(response.body().string()); } catch (IOException e) { e.printStackTrace(); }
Similar to before, we execute a multipart HTTP request where we can attach the desired file(s).
It’s possible to accidentally choose the wrong attachment when saving a to-do, so instead of waiting until the upload finishes, ensure the request can be cancelled any time and restarted with the right value later.
// same request as before Request postRequest = new Request.Builder() .url("https://mytodoserver.com/new") .post(requestBody) .build(); Call cancelableCall = client.newCall(postRequest); try { Response response = cancelableCall.execute(); System.out.println(response.body().string()); } catch (IOException e) { e.printStackTrace(); } // ... few seconds later from an other thread cancelableCall.cancel();
Now we have all the knowledge necessary for basic functionality in our app. We can check our to-do list, we can add new ones, and we can change their state.
Let’s look at the security side of our application.
Our backend had implemented a basic username/password-based authentication to avoid seeing and modifying each other’s to-dos.
Accessing our data now requires an Authorization
header to be set on our requests. Without that, the request could fail with a 401 Unauthorized
response.
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://mytodoserver.com/todolist") .addHeader("Authorization", Credentials.basic("username", "password")) .build(); try { Response response = client.newCall(request).execute(); System.out.println(response.body().string()); } catch (IOException e) { e.printStackTrace(); }
The addHeader()
method on the Request.Builder
will let us specify as many custom headers as we want.
Now our sensitive data is only accessible if someone knows our username and password. But what if someone is listening on the network and tries to hijack our requests with a man-in-the-middle attack and fabricated certificates?
OkHttp gives you an easy way to trust only your own certificate by using certificate pinner.
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.certificatePinner( new CertificatePinner.Builder().add( "mytodoserver.com","sha256/public_key_hash_of_my_certification" ).build() ); OkHttpClient client = clientBuilder.build();
Here we use OkHttpClient.Builder
to build a custom OkHttp client (more on this later). Then, with CertificatePinner
, we choose which certificates for which specific domains are trusted.
For more information on certificate pinning and security in general, please visit the relevant OkHttp documentation page.
If an issue occurs while making a request, we have to dig deeper into why it happened. OkHttp has its own internal APIs to enable debug logging, which can help. But we can also leverage on OkHttp’s interceptor API to make our life easier.
Interceptors can monitor, rewrite, and retry calls. We can use them to modify a request before it goes out, pre-process a response before it reaches our logic, or simply print out some details about the requests.
OkHttp has its own pre-made logging interceptor that we can just import via Gradle:
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
And to use it:
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build();
Or we can implement our own custom interceptor:
static class BasicLoggingInterceptor implements Interceptor { @NotNull @Override public Response intercept(Interceptor.Chain chain) throws IOException { Request request = chain.request(); System.out.println(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers())); Response response = chain.proceed(request); System.out.println(String.format("Received response for %s %n%s", response.request().url(), response.headers())); return response; } } // ... // usage later on OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new BasicLoggingInterceptor()) .build();
We can also declare our interceptors on application and network-level too based on our needs. You can read more about this here.
Sometimes it is useful to manipulate the responses of our backend API. We can achieve this by manipulating the server-side code, but it is more efficient via a proxy server.
We can use a system-wide proxy configuration on the device itself or instruct our OkHttp client to use one internally.
Proxy proxyServerOnLocalNetwork = new Proxy( Proxy.Type.HTTP, new InetSocketAddress("192.168.1.100", 8080) // the local proxy ); OkHttpClient client = new OkHttpClient.Builder() .proxy(proxyServerOnLocalNetwork) .build();
After we have debugged our application, you may have noticed that we complete a lot of unnecessary requests that put extra load on our server. There is no need to fetch the to-do list again if there was no change on the backend.
There is a default cache implementation in OkHttp where we only need to specify the cache location and its size, like so:
OkHttpClient client = new OkHttpClient.Builder() .cache(new Cache(new File("/local/cacheDirectory"), 10 * 1024 * 1024)) //10 MB .build();
But you can get wild with it if you would like to customize the behavior.
If you have custom caching logic, you could also implement your own way of caching. For example, you could execute a HEAD
request first to your server, then check the cache indication headers, and, if there was a change, execute a GET
request to the same URL to fetch the content.
We’ve already covered some usage of OkHttpClient.Builder
. This class is useful if we would like to alter the default OkHttp client behavior.
There are some parameters worth mentioning:
OkHttpClient client = new OkHttpClient.Builder() .cache(cache) // configure cache, see above .proxy(proxy) // configure proxy, see above .certificatePinner(certificatePinner) // certificate pinning, see above .addInterceptor(interceptor) // app level interceptor, see above .addNetworkInterceptor(interceptor) // network level interceptor, see above .authenticator(authenticator) // authenticator for requests (it supports similar use-cases as "Authorization header" earlier .callTimeout(10000) // default timeout for complete calls .readTimeout(10000) // default read timeout for new connections .writeTimeout(10000) // default write timeout for new connections .dns(dns) // DNS service used to lookup IP addresses for hostnames .followRedirects(true) // follow requests redirects .followSslRedirects(true) // follow HTTP tp HTTPS redirects .connectionPool(connectionPool) // connection pool used to recycle HTTP and HTTPS connections .retryOnConnectionFailure(true) // retry or not when a connectivity problem is encountered .cookieJar(cookieJar) // cookie manager .dispatcher(dispatcher) // dispatcher used to set policy and execute asynchronous requests .build();
For the complete list, please visit the documentation.
Thinking about a collaborative to-do list? Or notifying users once a new to-do is added? How about having a real-time chat over a to-do item? OkHttp’s got you covered here, too.
If you are done with the WebSocket server-side implementation, you can connect to that endpoint and get real-time messaging up and running from an OkHttp client.
OkHttpClient client = new OkHttpClient(); String socketServerUrl = "ws://mytodoserver.com/realtime"; Request request = new Request.Builder().url(socketServerUrl).build(); // connecting to a socket and receiving messages client.newWebSocket(request, new WebSocketListener() { @Override public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosed(webSocket, code, reason); //TODO: implement your own event handling } @Override public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosing(webSocket, code, reason); //TODO: implement your own event handling } @Override public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { super.onFailure(webSocket, t, response); //TODO: implement your own event handling } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { super.onMessage(webSocket, text); //TODO: implement your own event handling for incoming messages } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) { super.onMessage(webSocket, bytes); //TODO: implement your own event handling for incoming messages } @Override public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { super.onOpen(webSocket, response); //TODO: implement your own event handling } }); // sending message webSocket.send("new_todo_added");
We can’t forget about testing. OkHttp delivers its own MockWebServer to help test HTTP and HTTPS network calls. It lets us specify which response to return to which request and verifies every part of that request.
To start, we need to import it via Gradle:
testImplementation("com.squareup.okhttp3:mockwebserver:4.9.1")
Here are some important APIs:
MockWebServer.start()
: starts the mock web server on the local hostMockWebServer.enqueue(mockResponse)
: queues a MockResponse
. This is a FIFO queue that ensures the requests will receive responses in order as there were queuedMockResponse
: a scriptable OkHttp responseRecordRequest
: an HTTP request that was received by the MockWebServer
MockWebServer.takeRequest()
: takes the next request arrived to the MockWebServer
Once we understand the basics we can write our first test. Now, for a basic GET request:
public class MockWebServerTest { final MockWebServer server = new MockWebServer(); final OkHttpClient client = new OkHttpClient(); @Test public void getRequest_Test() throws Exception { final String jsonBody = "{'todo_id': '1'}"; // configure a MockResponse for the first request server.enqueue( new MockResponse() .setBody(jsonBody) .addHeader("Content-Type", "application/json") ); // start the MockWebServer server.start(); // create a request targeting the MockWebServer Request request = new Request.Builder() .url(server.url("/")) .header("User-Agent", "MockWebServerTest") .build(); // make the request with OkHttp Call call = client.newCall(request); Response response = call.execute(); // verify response assertEquals(200, response.code()); assertTrue(response.isSuccessful()); assertEquals("application/json", response.header("Content-Type")); assertEquals(jsonBody, response.body().string()); // verify the incoming request on the server-side RecordedRequest recordedRequest = server.takeRequest(); assertEquals("GET", recordedRequest.getMethod()); assertEquals("MockWebServerTest", recordedRequest.getHeader("User-Agent")); assertEquals(server.url("/"), recordedRequest.getRequestUrl()); } }
In short, OkHttp is a powerful library that offers plenty of perks, including HTTP/2 support, recovery mechanism from connection problems, caching, and modern TLS support.
If you have ever tried to implement these functionalities from scratch via the default Android and Java network APIs, you know how much work and pain it is (and how many edge cases that you forgot to cover). Luckily, implementing networking in your application with OkHttp makes this easy.
For more details, please visit the project page and GitHub. You can find some useful extensions, implementation samples, and testing examples.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.