OkHttp's Ins and Outs (Part 2)
This is the second part of OkHttp tutorial. You can find the first part of tutorial in the link here.
Previously, I have posted about http request history, benefits of OkHttp, and lots of basic examples for post/get request. In the second part of the tutorial, I will focus more on advanced features of OkHttp including management of timeouts, custom headers, uploading files and cancelling the calls.
1. File Uploading
1.1 Upload a file
In this example, we will see how to upload the raw text file. To upload raw text file in the server, we need MultipartBody.Builder class to create request body as shown in the example.
public void uploadFile(String url) throws IOException {
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file","filename.txt",
RequestBody.create(MediaType.parse("application/octet-stream")
, new File("src/test/resources/filename.txt")))
.build();
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
}
As you can see from the example, we have created multipart body and passed it into request, then called the request from client. In multipart body, you can customize your media types for your preference.
1.2 Get File Upload Progress
In most applications, specially in android application development it is very necessary to show the upload progress to inform the users and to actually show that upload is in progress. In this example, I will show how to get progress report. To realize it, as usual we need to create multipart request body and extend the request body to gain visibility into the upload progress as shown below
//Getting progress report
public void getProgressReport(String url) throws IOException {
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file","filename.txt",
RequestBody.create(MediaType.parse("application/octet-stream"),
new File("src/test/resources/filename.txt")))
.build();
/*We need to create progress listener here to give it to progress
wrapper later on*/
ProgressRequestWrapper.ProgressListener listener = (byteWritten, contentLength) ->{
float percentage = 100f * byteWritten / contentLength;
};
ProgressRequestWrapper counting = new ProgressRequestWrapper(requestBody, listener);
Request request = new Request.Builder()
.url(url)
.post(counting)
.build();
Response response = okHttpClient.newCall(request).execute();
}
As you can see here, we need to create ProgressListener interface to enable us to observe the upload progress
public interface ProgressListener{
void onRequestProgress(long bytesWritten, long contentLength);
}
And here is the ProgressRequestWrapper which is the extended version of RequestBody
class ProgressRequestWrapper extends RequestBody{
@Override
public MediaType contentType() {
return null;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
BufferedSink bufferedSink;
countingSink = new CountingSink(sink);
bufferedSink = Okio.buffer(countingSink);
delegate.writeTo(bufferedSink);
bufferedSink.flush();
}
}
protected class CountingSink extends ForwardingSink{
private long bytesWritten = 0;
public CountingSink(Sink delegate) {
super(delegate);
}
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
bytesWritten +=byteCount;
listener.onRequestProgress(bytesWritten,contentLength());
}
}
Here we have extended ForwardingSink to CountingSink, and overridden write() method to count the written bytes. Then, we have extended RequestBody to ProgressRequestWrapper and overridden writeTo () method to use ForwardingSink.
2. Setting a Custom Header
2.1 Setting a header on a Request
To set any custom header on a request, we can use a simple addHeader call in Request instance as you will see in the example below
public void addCustomHeader(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
//Adding custom header
.addHeader("Content-Type","application/json")
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
response.close();
}
2.2 Setting a Default Header
It is possible to configure a default header on client itself instead of setting it on each and every request. Here if we want to set content type for every request, all we have to do is to set interceptor. In our example is DefaultContentInterceptor which extends Interceptor.
public void setDefaultHeader(String url) throws IOException {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new DefaultContentInterceptor("application/json"))
.build();
Request request = new Request.Builder()
.url(url)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
}
class DefaultContentInterceptor implements Interceptor{
private String contentType;
public DefaultContentInterceptor(String contentType){
this.contentType = contentType;
}
@Overridepublic Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request requestWithUserAgent = request.newBuilder()
.header("Content-Type","your content type")
.build();
return chain.proceed(requestWithUserAgent);
}
}
3. Disable Redirects
In the section we will learn how to configure OkHttpClient to disable follow redirect. As we all know, by default if a GET request is answered with HTTP 301 Moved Permanently, the redirect is automatically followed. In some cases, that may be perfectly fine but there might be a situations when we do not want it.
To achieve this, we simply set followRedirects to false when we are building our client. Here is simple an example.
public void disableRedirect(String url) throws IOException {
OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
.followRedirects(false).build();
Request request = new Request.Builder()
.url(url)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
}
4. Timeouts
We can use the timeouts to fail a call when its peer is unreachable. There can be many reasons for network failures, including client connectivity problem, server availability, and something else. OkHttp supports timeouts for connect, read, and write. Here is example for read timeout of 1 second.
public void readTimeoutExample(String url) throws IOException {
OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(1,
TimeUnit.SECONDS).build();
Request request = new Request.Builder()
.url(url)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
}
You need to know that test will fail if client timeout is lower than resource response time.
5. Cancelling a Call
To cancel a call, you can simple cancel the call by call.cancel() method. And it is important to know that if a thread is currently writing a request or reading a response, an IOException will be thrown.
We can use this feature to conserve network when a call is no longer necessary for example user navigates away from an application.
6. Response Caching
To create a cache, we need a cache directory from where we can read and write to. The client will use it to cache the response. Besides, we need to have limit on cache size.
public void caching(String url) throws IOException {
int cacheSize = 10 * 1024 * 1024;
File cacheDirectory = new File("src/test/resources/cache");
Cache cache = new Cache(cacheDirectory,cacheSize);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(cache)
.build();
Request request = new Request.Builder()
.url(url)
.build();
Response response1 = okHttpClient.newCall(request).execute();
// response1.cacheResponse();
Response response2 = okHttpClient.newCall(request).execute();
}
After first test execute(), the response from the first call will not have been cached, a call to the method cacheResponse() will return null, while a call to the method networkResponse() will return the response from the network. And the cache folder will be filled with cache files.
The second call execution will produce the opposite effect, since the response will have already been cached, it means a call to networkResponse() will return null while a call to cacheResponse() will return the response from the cache;
To prevent the response from using the cache, use CacheControl.FORCE_NETWORK. To prevent it from using the network, use CacheControl.FORCE_CACHE. You need to be careful if you use FORCE_CACHE and the response requires the network, and OKHTTP will return a 504 Unsatisfied Request response.
Last Words
In the last two series of tutorials we saw lots of examples of using OkHttp. Just keep practicing and learning until it becomes second nature. In the next tutorials, I will be posting an article about Retrofit library, and a little bit advanced version of OkHttp client.
Good Luck,
Thank you for reading it.
Software Engineer, Google
6 年Good job! Keep it up :)