okhttp3.Call的enqueue()方法沒有回到callback,可能是maxRequestsPerHost引起

    這段時間在全球範圍的新型冠狀病毒蔓延之下,忙忙碌碌之中,已有4個月的時間沒寫新的東西了,慚愧慚愧!

    這次是記錄一下項目中遇到的一個在多次網絡請求之後,網絡接口沒有回調的問題,下面說一下具體的來龍去脈。

    有一個文件下載的需求:最多同時支持3個下載任務。在明確了需求之後,就開始設計文件下載的方案了。

    1、網絡請求、數據寫入磁盤等需要異步處理;

    2、有可能存在比較大的文件,所以拆分爲多個線程進行分段下載

    3、支持斷點續傳

    4、下載過程中的異常恢復,例如網絡切換等等

    當然還有其他細節需要處理,例如進度更新等等,這裏就不多說了,這次主要說說多線程下載過程中遇到的問題。我的網絡請求是這樣的,先創建一個OkHttpClient對象

        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS);
        OkHttpClient mOkHttpClient = builder.build();

    然後就是進行分段下載的網絡請求了,當然在請求之前,要先計算好每段的起始位置和結束位置,我這裏只闡述請求的部分:

        // 設置分段下載的頭信息
        // start:起始位置,end:結束位置
        Request request = new Request.Builder().header("RANGE", "bytes=" + start + "-" + end)
                .url(url)// 文件的網絡地址
                .build();
        // 異步請求
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(callback);// 這個callback是okhttp3.Callback類型的對象

    網絡會回調上面的callback,這個callback裏要實現兩個方法:

  void onFailure(Call call, IOException e);
  void onResponse(Call call, Response response) throws IOException;

    網絡請求成功,則回調onResponse方法,在這裏執行網絡數據讀取和寫入磁盤就行了,若失敗,則回調onFailure方法。感覺、似乎成功的曙光就在眼前了,結果在發起3個文件下載的任務後,發現只有5個網絡請求回調了onResponse方法。正常情況下,3個文件下載任務,每個任務分3個請問進行分段下載,按說應該有9個回調onResponse方法。心塞啊。

    遇到問題,先從自身找原因吧。網絡請求方式、分段的參數是沒問題,那問題應該就在異步請求enqueue()上面了,跳進這個方法一步一步跟進去會發現,它會調用到okhttp3.Dispatcher類的promoteAndExecute方法:

private boolean promoteAndExecute() {
    ......
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
        ......
    }
    ......
    return isRunning;
  }

    這個方法裏的for循環體裏有2個if語句,第一個if語句是說最大併發請求數不能超過maxRequests個,第二個if語句是說每個主機最大請求數不能超過maxRequestsPerHost個,這兩個值的默認大小分別是

/** 最大併發請求數*/
private int maxRequests = 64;
/** 每個主機最大請求數*/
private int maxRequestsPerHost = 5;

    因爲我下載的文件的主機地址是相同的,並且我發起了9個請求,於是我最多隻能同時接收到5個回調。在明白的原因後,就要修改這個值,修改方法很簡單:

mOkHttpClient.dispatcher().setMaxRequestsPerHost(9);

   然後,就沒有然後了,問題就此搞定了。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

------記錄工作中的點點滴滴,書寫碼農的平凡歲月!------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章