这段时间在全球范围的新型冠状病毒蔓延之下,忙忙碌碌之中,已有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);
然后,就没有然后了,问题就此搞定了。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
------记录工作中的点点滴滴,书写码农的平凡岁月!------