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);

   然后,就没有然后了,问题就此搞定了。

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

------记录工作中的点点滴滴,书写码农的平凡岁月!------

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