OkHttp优雅的实现下载监听

OkHttp优雅的实现下载监听

(ps:好久好久没有发什么博文了,打字都要锈了,请容许我在这里水一波~~)
咱们都知道okhttp的运转原理,经过interceptor拦截器一层一层嵌套执行。要实现下载监听,经过如下代码
@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实现下载监听。

bash

问题

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

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。ide

重点来了

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

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

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

解决办法

根据okhttp拦截器嵌套执行的逻辑,咱们也能够模拟一个本身的拦截器的嵌套执行,而后和默认的拦截器串联在一块儿,来实现咱们想要的逻辑。spa

/**
 * 网络链接代理拦截器
 * 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),嵌套进默认的拦截器执行集合里,这样也就能够实现对拦截器的动态管理了~~.net

结束语

既然咱们使用了okhttp,大几率也是要用到拦截器(除了下载监听,还有统一header设置,或者统一的错误码判断等),若是你的拦截器只有一段代码用到,其余地方不想用,能够试试这样的代理方式,方便动态管理。3d

相关文章
相关标签/搜索