转载

OkHttp优雅的实现下载监听

@Override
public Response intercept(Chain chain) throws IOException {
    Response response = chain.proceed(chain.request());
    return response.newBuilder().body(new ProcessableResponse(response.body(), progressListeners)).build();
}
复制代码

okhttp添加拦截器,传入带有监听的ResponseBody实现下载监听。

问题

既然核心是传入带有监听的ResponseBody,那么能不能在enqueue(Callback callback)回调里传入带有监听的responseBody呢(也就是以下代码)

call.enqueue(new Callback() { 
   @Override
   public void onResponse(Call call, Response response) throws IOException {
       response.newBuilder().body(new ProcessableResponse(response.body(), progressListeners)).build();
   }
})
复制代码

行吗[假装] ???

当然不行[假装豁然开朗]~~

OkHttp优雅的实现下载监听

从这张执行图很清晰的可以看到,HttpLoggingInterceptor已经执行了,而这个拦截器其实是我最先设置进去的,也就是说拦截器递归执行返回的时候已经走完了所有的拦截器,自然我们设置的监听的拦截器也被执行了,最终卡在了Okio的read方法里~~

结论~~

也就是说,在执行call的回调之前,Okio已经在读取数据了,如果我们想要对下载进行监听,就必须在读取数据之前,把默认的responseBody包装成我们能监听数据读取的responseBody。

重点来了

既然要用拦截器来实现下载监听,一般是要在okhttp初始化阶段,而我们的下载监听实际上就只有下载数据的时候需要用到,也就是说我想在要下载的时候才设置我们的下载监听,下载完了,把它移除,但是↓

OkHttpClient(Builder builder) {
   ...
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    ...
  }
复制代码

上面代码可以看到,okhttp在创建时就把拦截器设置成了不可修改的了,也就是说,后续我们就不能动态的添加拦截器了~~

解决办法

根据okhttp拦截器嵌套执行的逻辑,我们也可以模拟一个自己的拦截器的嵌套执行,然后和默认的拦截器串联在一起,来实现我们想要的逻辑。

/**
 * 网络连接代理拦截器
 * 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 Request request() {
            return realInterceptorChain.request();
        }

        @Override
        public Response proceed(@NonNull Request request) throws IOException {
            final Chain next;
            if (index + 1 >= realInterceptors.size()) {// 把代理拦截器与原本的拦截器相连接
                next = realInterceptorChain;
            } else {
                next = new TempInterceptorChain(realInterceptorChain, realInterceptors, index + 1);
            }
            Interceptor interceptor = realInterceptors.get(index);
            return interceptor.intercept(next);// 内部继续执行process 形成递归嵌套
        }

        @Override
        public Connection connection() {
            return realInterceptorChain.connection();
        }
    }
}

复制代码

以上就是我们的代理拦截器,可以我们传入的拦截器集合(realInterceptors),嵌套进默认的拦截器执行集合里,这样也就可以实现对拦截器的动态管理了~~

结束语

既然我们使用了okhttp,大概率也是要用到拦截器(除了下载监听,还有统一header设置,或者统一的错误码判断等),如果你的拦截器只有一段代码用到,其他地方不想用,可以试试这样的代理方式,方便动态管理。

原文  https://juejin.im/post/5b7a8f65f265da431d0e60b2
正文到此结束
Loading...