URLSession如何动态控制并发数?
URLSession系列接口对我们的日常开发提供了很大的方便,我们不需要创建和维护队列就可以快速的发起一个网络请求。
但是凡事有利就有弊,封装的越多,我们能够控制的就越少。
起因
最近部门内在对App进行优化,打算将已经使用了五年的网络库从NSURLConnection系列接口切换到URLSession,需要重构。针对弱网的优化也是这次考虑的重点。
因为运营商会控制长连接的连接个数(2G是一个,3G是两个,4G和wifi是四个),所以需要监听网络状态动态的修改URLSession的并发数。
问题
NSURLSession通过NSURLSessionConfiguration传入配置信息并初始化对象。
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
问题在于NSURLSessionConfiguration
在init时是通过深拷贝保存的,即使修改外面对象的HTTPMaximumConnectionsPerHost
也无济于事,没法再次修改。
解决方案
为了能够自主控制并发数,只能做一层封装,添加一个请求队列,手动控制并发数。
这里借鉴了AFNetworking中AFImageDownloader的实现。
步骤
- 初始化NSURLSession时,NSURLSessionConfiguration的HTTPMaximumConnectionsPerHost设置成 4 (使用
maximumActiveDownloads
变量保存初始值,以后通过修改该值控制并发数); - 定义一个成员变量
activeRequestCount
,计算当前的发起请求的个数,初始值为0; - 每次发起请求前,首先判断当前请求的个数activeRequestCount是否小于maximumActiveDownloads:
- (BOOL)isActiveRequestCountBelowMaximumLimit {
return self.activeRequestCount < self.maximumActiveDownloads;
}
- 发起请求的方法中进行判断,如何立即请求,activeRequestCount +1,否则保存到数组队列中:
if ([self isActiveRequestCountBelowMaximumLimit]) {
[self startMergedTask:mergedTask];
} else {
[self enqueueMergedTask:mergedTask];
}
```
5. 请求完成的回调函数中对activeRequestCount -1,并判断是否发起下一次请求:
[strongSelf safelyDecrementActiveTaskCount];
[strongSelf safelyStartNextTaskIfNecessary];
```
这样当我们修改maximumActiveDownloads等于1或者2时,如果当前activeRequestCount 大于等于2,就不会继续发送请求,等到activeRequestCount < maximumActiveDownloads
才会发送下个请求。
AFNetworking的源码很值得一读,读的时候可以想想AFNetworking对URLSession是怎样封装的?为什么要这样封装?这样封装的优点是什么?如果让你设计,你会怎么做?