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.
Key Takeaways
- OkHttp is an Android HTTP client library from Square that simplifies networking in apps, reducing the number of steps needed and handling common connection problems. It supports Android 2.3 and above and is currently at version 3.3.1.
- To use OkHttp, dependencies need to be added in the build.gradle file and internet permission added in AndroidManifest.xml. The library allows for GET and POST network requests, file uploads, and handling of cookies.
- OkHttp requires network calls to be handled asynchronously in Android to avoid a NetworkOnMainThreadException. This can be achieved using AsyncTask. The library also allows for the monitoring of request progress, which can be useful when uploading media.
- OkHttp includes a CookieJar interface for handling cookies. An OkHttpClient instance can be updated to accept cookies, and all network request cookies will be handled according to the policies implemented in the CookieStore class. For persistent cookies through subsequent app launches, the PersistentCookieJar implementation based on SharedPreferences can be used.
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 RequestBody
s.
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 RequestBody
s 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 AsyncTask
s, 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.
Valdio recently graduated in Computer Engineering. He is a mobile developer, who is passionate about mobile technologies and learning new things. He has worked with languages such as C, Java, php and is currently focused on Java, mobile and web development