Elegant implementation of OkHttp to download listeners

(PS: I haven’t posted anything for a long time, my typing is getting rusty, please allow me to take a wave here ~~)
We all know how okHTTP works. It is provided by interceptor, which is nested one layer at a time. To implement the download listener, use the following code
@Override
public Response intercept(Chain chain) throws IOException {
    Response response = chain.proceed(chain.request());
    return response.newBuilder().body(new ProcessableResponse(response.body(), progressListeners)).build();
}
Copy the code

Okhttp adds an interceptor, passing in the ResponseBody with the listener to download the listener. Left left

The problem

If the ResponseBody is the ResponseBody with the listener, then the ResponseBody is the ResponseBody with the listener in the enqueue(Callback Callback) Callback.

call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { response.newBuilder().body(new ProcessableResponse(response.body(), progressListeners)).build(); }})Copy the code

Can you [pretend]??

Of course not

Conclusion ~ ~

If we want to listen to the download, we must wrap the default responseBody as the responseBody we can listen to before we read the call callback.

The key to the

Since we want to use interceptor to implement download listening, generally in the okHTTP initialization phase, and our download listening is actually only required to download data, that is, I want to set up our download listening, download finished, remove it, but ↓

OkHttpClient(Builder builder) { ... this.interceptors = Util.immutableList(builder.interceptors); this.networkInterceptors = Util.immutableList(builder.networkInterceptors); . }Copy the code

As you can see from the above code, okHTTP was created with the interceptors set to be unmodifiable, which means that we cannot dynamically add interceptors later

The solution

According to the okHTTP interceptor nested execution logic, we can also simulate a nested execution of our own interceptors and then concatenate them with the default interceptors to implement the desired logic.

/** ** Created by Yan on 8/20/18. */ class ProxyNetInterceptor implements Interceptor {private List<Interceptor> realInterceptors = new ArrayList<>(); @Override public Response intercept(@NonNull Chain chain) throws IOException {if(! realInterceptors.isEmpty()) { TempInterceptorChain tempInterceptorChain = new TempInterceptorChain(chain, realInterceptors, 0);return tempInterceptorChain.proceed(chain.request());
        }
        return chain.proceed(chain.request());
    }

    public void addRealInterceptor(Interceptor realInterceptor) {
        if(! realInterceptors.contains(realInterceptor)) { realInterceptors.add(realInterceptor); } } public void removeNetInterceptor(Interceptor netInterceptor) { realInterceptors.remove(netInterceptor); } private static class TempInterceptorChain implements Interceptor.Chain { private Chain realInterceptorChain; private List<Interceptor> realInterceptors; private int index; private TempInterceptorChain(Chain realInterceptorChain, List<Interceptor> realInterceptors, int index) { this.realInterceptorChain = realInterceptorChain; this.realInterceptors = realInterceptors; this.index = index; } @Override public Requestrequest() {
            return realInterceptorChain.request();
        }

        @Override
        public Response proceed(@NonNull Request request) throws IOException {
            final Chain next;
            if(index + 1 >= realInterceptors.size()) {// Connect the proxy interceptor to the original interceptor next = realInterceptorChain; }else {
                next = new TempInterceptorChain(realInterceptorChain, realInterceptors, index + 1);
            }
            Interceptor interceptor = realInterceptors.get(index);
            returninterceptor.intercept(next); } @override public Connectionconnection() {
            returnrealInterceptorChain.connection(); }}}Copy the code

This is our proxy interceptors. We can pass in a set of realInterceptors, nested in the default interceptor execution set, so that we can dynamically manage interceptors

conclusion

Since we are using okHTTP, we are likely to use interceptors (in addition to downloading listeners, there are also uniform header Settings, or uniform error code determination, etc.). If your interceptors are only used for one piece of code, and you don’t want to use them for the rest of the code, you can try this proxy for easy dynamic management.