AFNetworking框架分析(三)——AFURLSessionManager(下)

时间:2022-06-19
本文章向大家介绍AFNetworking框架分析(三)——AFURLSessionManager(下),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

上一篇讲到在AFHTTPSessionManager中,在初始化NSMutableURLRequest对象时的流程分析。接下来继续分析在生成request之后AFN创建task任务的流程 在NSMutableURLRequest对象初始化之后,创建了一个NSURLSessionDataTask任务类对象,并将request传入。

创建task任务代码

层层代码实现,最终会找到- (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对应的cancelpauseresume这三个状态,正好对应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进度条的状态之类等等。

监听上传、下载progress属性的KVO处理方法实现

以上就是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调度组可以最快捷的实现该功能)

dispatch_group调度组的使用

利用三位运算符,判断如果没有实现自定义的completionGroup和completionQueue,那么就使用AFNetworking提供的私有的dispatch_group_t和dispatch_get_main_queue线程。在调度组中执行AFURLSessionTaskCompletionHandler代码块并将task.response, responseObjecterror返回出去。 当前不存在error时,也就是task任务成功执行,会首先创建一个并发队列,用于在网络请求任务完成后处理数据的,并发队列实现多线程处理多个请求完成后的数据处理,并对数据进行一次序列化操作。

数据序列化

根据对应的task和data将response data解析成可用的数据格式,比如JSON serializer就将data解析成JSON格式。序列化完成之后与error存在时数据处理逻辑相同,最终通过执行AFURLSessionTaskCompletionHandler代码块,根据代码块中的数据,返回至外层方法,判断执行失败block还是成功block。 从AFN框架中,可以发现AFURLSessionManagerTaskDelegateNSURLSessionTask都是通过AFURLSessionManager来进行生成、绑定、销毁等管理操作的。