AFNetworking框架分析(三)——AFURLSessio
上一篇讲到在AFHTTPSessionManager中,在初始化NSMutableURLRequest对象时的流程分析。接下来继续分析在生成request之后AFN创建task任务的流程
在NSMutableURLRequest对象初始化之后,创建了一个NSURLSessionDataTask任务类对象,并将request传入。
层层代码实现,最终会找到
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
方法实现。
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
//设置回调块
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
此处的代码意义为将一个session task和一个AFURLSessionManagerTaskDelegate类型的delegate变量绑在一起,而这个绑在一起的工作是由我们的AFURLSessionManager所做。至于绑定的过程,就是以该session task的taskIdentifier为key(taskIdentifier是在创建task的时候NSURLSessionTask为其设置的,不需要手动设置,保证唯一性),delegate为value。赋值给mutableTaskDelegatesKeyedByTaskIdentifier这个NSMutableDictionary类型的变量,以此来确保task唯一。[self setDelegate:delegate forTask:dataTask]
方法就是将两者进行绑定,查看其实现代码
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
//task和delegate都不能为空
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
[self.lock lock]
与[self.lock unlock]
方法用于确保中间代码块为原子操作,保证线程安全。然后将delegate存入字典,以task的id作为key,说明每个task都有各自的代理。
接着,[delegate setupProgressForTask:task]
方法设置两个NSProgress的变量 - 上传uploadProgress和下载downloadProgress,给session task添加了监听KVO事件,用于实时监听返回上传、下载progress的更新进度。其内部实现中设置这两个NSProgress对应的cancel
、pause
和resume
这三个状态,正好对应session task的cancel、suspend和resume三个状态。然后给task的当前接收大小、期望接收大小、当前发送大小以及期望发送大小四个属性进行KVO监听,和上传、下载progress的fractionCompleted
属性进行KVO监听(fractionCompleted属性代表当前progress已经完成的比例,取值范围0-1)。当task任务的进度发生变化时,KVO则更新对应的progress属性值,然后赋新值触发时会执行KVO代理中上传或者下载progress的处理,将执行对应的包含object(属性为NSProgress)的代码块,该代码块也就是最终执行网络请求方法中progress:(void (^)(NSProgress * _Nonnull))uploadProgress
位置的block代码块。此处代码块中可根据NSProgress的状态做用户自定义的行为,比如需要更新UI进度条的状态之类等等。
以上就是AFN的请求过程进度更新返回的内部实现。
这里为什么要在AFN内部添加delegate,并将其和task进行一一绑定?
在上面的更新进度状态时操作,完全可以放到AFURLSessionManager核心类本身中进行执行,但这样全部放在同一类下处理会不断增加核心类的复杂度。因此将请求过程与完成,交给delegate去处理,提高可维护性。
当task任务执行resume方法开始请求网络后,会执行NSURLSession相关的代理方法。
当收到返回数据时,会执行- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
代理方法。该代理方法可能会在收到数据时多次执行,因此需要拼接其中的data数据。
当task任务完成之后,不管请求成功还是失败,都会执行- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
代理方法,而此处返回的error并不是服务端的error,而是客户端本身的error,例如网络不可用、访问地址不可达等等。
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
// AFN作者说,task若在后台完成,可能delegate会为nil
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
// task结束,就移除对应的delegate
[self removeDelegateForTask:task];
}
//自定义Block回调
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
看下delegate在task完成时[delegate URLSession:session task:task didCompleteWithError:error]
做了什么操作。
这时里面代码非常多,但是目的很简单,就是将网络请求相关的所有数据添加至名为userInfo字典中,并最终通过AFURLSessionTaskCompletionHandler代码块返回出去。
打印下userInfo,可以发现字典中存储的key值以及意义:
1. AFNetworkingTaskDidCompleteResponseDataKey
session 存储task获取到的原始response数据,与序列化后的response有所不同
2. AFNetworkingTaskDidCompleteSerializedResponseKey
存储经过序列化(serialized)后的response
3. AFNetworkingTaskDidCompleteResponseSerializerKey
保存序列化response的序列化器(serializer)
4. AFNetworkingTaskDidCompleteAssetPathKey
存储下载任务后,数据文件存放在磁盘上的位置。若downloadFileURL存在,则保存downloadFileURL地址;若不存在,则取出self.mutableData中的data数据保存。
5. AFNetworkingTaskDidCompleteErrorKey
错误信息
当userInfo字典全部保存完成后,首先判断是否存在error。若存在error则说明task任务出错,需要处理并返回出error信息。此处运用到了GCD中dispatch_group调度组(通常在项目中需要指定多个任务全部完成后再执行其它任务时,使用dispatch_group调度组可以最快捷的实现该功能)
利用三位运算符,判断如果没有实现自定义的completionGroup和completionQueue,那么就使用AFNetworking提供的私有的dispatch_group_t和dispatch_get_main_queue线程。在调度组中执行
AFURLSessionTaskCompletionHandler
代码块并将task.response
, responseObject
和error
返回出去。当前不存在error时,也就是task任务成功执行,会首先创建一个并发队列,用于在网络请求任务完成后处理数据的,并发队列实现多线程处理多个请求完成后的数据处理,并对数据进行一次序列化操作。
数据序列化
根据对应的task和data将response data解析成可用的数据格式,比如JSON serializer就将data解析成JSON格式。序列化完成之后与error存在时数据处理逻辑相同,最终通过执行
AFURLSessionTaskCompletionHandler
代码块,根据代码块中的数据,返回至外层方法,判断执行失败block还是成功block。从AFN框架中,可以发现
AFURLSessionManagerTaskDelegate
与NSURLSessionTask
都是通过AFURLSessionManager
来进行生成、绑定、销毁等管理操作的。