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

相關文章
相關標籤/搜索