OkHttp's Ins and Outs (Part 2)

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.







Vohid Karimov

Software Engineer, Google

6 年

Good job! Keep it up :)

要查看或添加评论,请登录

Farruh Habibullaev的更多文章

  • Good API architecture principles

    Good API architecture principles

    Claimer: This article is purely written from my own experience in software development and design, however in some…

  • Practical Dijkstra's Algorithm

    Practical Dijkstra's Algorithm

    In computer science and discrete mathematics, we have encountered the concept of "single - source shortest path" many…

  • Android Core: Looper, Handler, and HandlerThread

    Android Core: Looper, Handler, and HandlerThread

    I have posted this article for those who don't have any idea how multi-threading architecture in android works, and…

  • OkHttp's Ins and Outs (Part 1)

    OkHttp's Ins and Outs (Part 1)

    This is the first part of OkHttp tutorial. 1.

    4 条评论
  • Creational Design Patterns

    Creational Design Patterns

    Creational design patterns are one of three categories of design patterns (creational, structural, and behavioral…

  • Decorator and Proxy Pattern

    Decorator and Proxy Pattern

    Decorator pattern Decorator pattern is one of the structural design patterns and used to modify the functionality of…

  • S.O.L.I.D Principles in OO programming

    S.O.L.I.D Principles in OO programming

    SOLID is the first five essential principles of object oriented programming for building a well designed, working…

  • Mediator and Command Pattern

    Mediator and Command Pattern

    Mediator Pattern Mediator pattern falls into category of behavioral patterns since it deals with behavior of the…

  • Composite Design Pattern

    Composite Design Pattern

    Composite design patterns falls into one of structural design patterns. Before diving into the composite design…

    2 条评论
  • Iterator Design Pattern

    Iterator Design Pattern

    Iterator design pattern falls into the category of behavioral pattern. Iterator pattern is used to provide iteration…

社区洞察

其他会员也浏览了