How to Use Android OKHttp3 Interceptor

  • 2021-08-31 09:03:45
  • OfStack

This article introduces the use of Android OKHttp3 interceptor and shares it with you as follows:

Add Interceptor

In the previous article, we already know the basic use of okhttp. When introducing OkHttpClient initialization, we introduced two ways. The second way can set interceptors on this OkHttpClient object, as follows:


//  Configure 1 Some information enters OkHttpClient
mOkHttpClient = new OkHttpClient().newBuilder()
        .connectTimeout(REQUEST_TIME, TimeUnit.SECONDS)
        .readTimeout(REQUEST_TIME, TimeUnit.SECONDS)
        .writeTimeout(REQUEST_TIME, TimeUnit.SECONDS)
        .addInterceptor(new LoggerInterceptor())
        .build();

As in the above code, it is very simple. You can add interceptors by using addInterceptor method, while custom interceptors only need to implement Interceptor interface, as shown below:


public class LoggerInterceptor implements Interceptor {
   ...
}

Application scenario

Log printing

Interceptors can be used to easily print the logs that need to be viewed when network requests are made. As shown below:


public class LoggerInterceptor implements Interceptor {

  @Override
  public Response intercept(@NonNull Chain chain) throws IOException {
    //  Intercept the request and get the request
    Request request = chain.request();
    //  Perform this network request operation and return response Information 
    Response response = chain.proceed(request);
    if (Configuration.DEBUG) {
      for (String key : request.headers().toMultimap().keySet()) {
        LogUtil.e("zp_test", "header: {" + key + " : " + request.headers().toMultimap().get(key) + "}");
      }
      LogUtil.e("zp_test", "url: " + request.url().uri().toString());
      ResponseBody responseBody = response.body();

      if (HttpHeaders.hasBody(response) && responseBody != null) {
        BufferedReader bufferedReader = new BufferedReader(new
            InputStreamReader(responseBody.byteStream(), "utf-8"));
        String result;
        while ((result = bufferedReader.readLine()) != null) {
          LogUtil.e("zp_test", "response: " + result);
        }
        //  Test code 
        responseBody.string();
      }
    }
    //  Note that writing like this is equivalent to recreating Request Gets the new Response When executing the above code, 
    //  Called responseBody.string() It cannot be called again in the return body. 
    return response.newBuilder().build();
  }

}

A print verification was done: by printing the time and thread name of interceptor and return body respectively, we can know that they are in the same thread, and the time for requesting execution will also increase when interceptor is added. Therefore, it is guessed that the code in different interceptors is executed linearly, and an identical or new response is returned according to requirements.

Cache

To realize caching, add an extra line of code when creating okhttpclint. cache (), and set the cache directory through it. Of course, the server needs to support caching function.


mOkHttpClient = new OkHttpClient().newBuilder()
        .cache(new Cache(FileUtils.getCacheDirectory(AppApplication
            .getApplication(), ""), 1024 * 1024))
        .connectTimeout(REQUEST_TIME, TimeUnit.SECONDS)
        .readTimeout(REQUEST_TIME, TimeUnit.SECONDS)
        .writeTimeout(REQUEST_TIME, TimeUnit.SECONDS)
        .addNetworkInterceptor(new LoggerInterceptor())
        .build();

If caching is supported on the server side, the Response returned by the request will be set with the header information header: cache-control, max-age=xxx. At this time, you can use the caching function directly. Among them, the cache time set by max-age will not be used even if there is cache after this time.

The cache-related fields in the header information returned by my company's server are as follows:

header: {cache-control : [no-store, private]}

header: {pragma : [no-cache]}

This means that the server does not support caching by default, and okhttp will not cache this request.

Next, let's look at how to set cache in interceptor


public class LoggerInterceptor implements Interceptor {

  @Override
  public Response intercept(@NonNull Chain chain) throws IOException {
    //  Intercept the request and get the request
    Request request = chain.request();
    //  Perform this network request operation and return response Information 
    Response response = chain.proceed(request);
    if (Configuration.DEBUG) {
      for (String key : request.headers().toMultimap().keySet()) {
        LogUtil.e("zp_test", "header: {" + key + " : " + request.headers().toMultimap().get(key) + "}");
      }
      LogUtil.e("zp_test", "url: " + request.url().uri().toString());
      ResponseBody responseBody = response.body();
    }
   
    return response.newBuilder()
      //  Increase 1 Cache header information, cache time is 60s
      .header("cache-control", "public, max-age=60")
       //  Remove pragma Header information 
      .removeHeader("pragma")
      .build();
  }
}

This setting is tantamount to forcing the use of caching within 60s.

Note:

Remember, at the beginning, I made a mistake that okhttp3 couldn't cache post interface

The interceptor can be understood as giving the requested request and response a chance to re-encapsulate, so that you can give a specific interface or interface that meets a specific condition a special operation under specific conditions.

For example, there is a scenario, when there is a network, make a request, and when there is no network, take cached data.


if (NetUtils.isNetAvailable(AppApplication.getApplication())) { 
   response.newBuilder() 
        .header("Cache-Control", "public, max-age=" + 0) 
        .removeHeader("Pragma") 
        .build(); 
 } else { 
   int maxStale = 60 * 60 * 24; //  When there is no network, set the timeout to 1 Days 
   response.newBuilder() 
       .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) 
       .removeHeader("Pragma") 
       .build(); 
 } 

return response;

max-stale: In addition to the expiration time specified by max-age, an additional specified time can be added to use the expired response.

There are a lot of these practices on the Internet, but I tried it once in the interceptor, and when there is no network, I won't walk into the interceptor at all. (I'm using a network interceptor. If there is any other reason, please point out the error.)

The final solution is the judgment operation when initializing request (if you are not familiar with initialization, you can refer to the basic use of OKHttp3 in my last article), initialize normal request when there is a network, and initialize request that forcibly uses cache when there is no network:


Request request;
if (NetUtils.isNetAvailable(AppApplication.getApplication())) {
   request = addHeaderInfo().url(requestUrl).build();
} else {
   request = addHeaderInfo().url(requestUrl).cacheControl(CacheControl.FORCE_CACHE).build();
}

The interceptor is still in the form above, but the effective time is changed to 0


response.newBuilder() 
       .header("Cache-Control", "public, max-age=" + 0) 
       .removeHeader("Pragma") 
       .build();

This allows you to use up-to-date data with a network and cached data without a network.


Related articles: