@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(); } }) 复制代码
行吗[假装] ???
从这张执行图很清晰的可以看到,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设置,或者统一的错误码判断等),如果你的拦截器只有一段代码用到,其他地方不想用,可以试试这样的代理方式,方便动态管理。