赞
踩
上篇文章主要介绍了AFNetworking的简单介绍和使用,熟悉了AFNetworking的基本结构,那么本文我们主要介绍一下网络通信模块(AFURLSessionManger,AFHTTPSessionManager),首先我们来看看这两个类之间的关系,以及他们内部都包含哪些模块,用来做什么的,我们先来看一下下面这张图:
由上图可见,首先AFURLSessionManger是AFHTTPSessionManager的父类,其中AFURLSessionManger内部还包含AFURLSessionManagerTaskDelegate和_AFURLSessionTaskSwizzling;AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;
AFURLSessionManager负责生成对应的NSURLSession的实例,管理AFNetworkReachabilityManager和AFSecurityPolicy,以此一来查看网络的连接情况,二来保证请求的安全,同时初始化生成一个AFJSONResponseSerializer的实例来序列化HTTP的响应结果;
核心的方法包括:初始化方法;针对不同任务的request方法;
初始化方法的实现在上文已经详细讲过,最终生成一个AFURLSessionManager的实例对象;上文我们介绍了dataTaskWithRequest方法,下面我们介绍一下uploadTaskWithRequest、downloadTaskWithRequest和downloadTaskWithResumeData这三个方法:
//使用本地文件的指定请求创建一个NSURLSessionUploadTask - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { //根据request以及要上传的本地文件的URL创建一个uploadTask NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; //为uploadTask设置代理 if (uploadTask) { [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; } return uploadTask; }
//使用指定的HTTP body请求创建NSURLSessionUploadTask
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
//使用指定的流式处理请求创建NSURLSessionUploadTask
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
我们可以看到uploadTaskWithRequest根据不同的数据创建一个NSURLSessionUploadTask任务,但是最终都会走到addDelegateForUploadTask为对应的uploadTask设置代理;addDelegateForUploadTask这个方法上文具体讲述过;
接下来我们再看看downloadTaskWithRequest
//使用指定的请求创建NSURLSessionDownloadTask
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
downloadTaskWithRequest:使用指定的请求request来创建对应的NSURLSessionDownloadTask,并且为所创建的task设置代理,最终将其返回;
//创建重用数据的下载任务;使用已经下载的部分数据ResumeData创建一个下载任务,继续进行下载。
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData];
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
downloadTaskWithResumeData:使用已经下载的部分数据 resumeData 创建一个下载任务,继续进行下载。并且为所创建的task设置代理,最终将其返回;
uploadProgressForTask和downloadProgressForTask 这两个方法是用来获取上传或者下载的进度;另外还有一些自定义的block的set方法。关于这一点,里边使用到的一些自定义的block回调,作者在.m文件中声明一些block属性,并且复写了其set方法,然后又在.h文件中声明这些set方法:这样做的目的看来是为了使用方便,我们在调用set方法设置这些block,能够很清晰的看到block的各个参数与返回值;
上文最后提到AFURLSessionManager遵守的代理都有NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying;对应实现了这些代理中的一些方法,源代码如下:
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error; __strong AFURLSessionManager *manager = self.manager; __block id responseObject = nil; NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; //Performance Improvement from #2672 NSData *data = nil; if (self.mutableData) { data = [self.mutableData copy]; //We no longer need the reference, so nil it out to gain back some memory. self.mutableData = nil; } #if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) { if (self.sessionTaskMetrics) { userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics; } } #endif if (self.downloadFileURL) { userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; } else if (data) { userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; } if (error) { userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ if (self.completionHandler) { self.completionHandler(task.response, responseObject, error); } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); } else { dispatch_async(url_session_manager_processing_queue(), ^{ NSError *serializationError = nil; responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; if (self.downloadFileURL) { responseObject = self.downloadFileURL; } if (responseObject) { userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; } if (serializationError) { userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; } dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ if (self.completionHandler) { self.completionHandler(task.response, responseObject, serializationError); } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); }); } }
遵守的这些代理,最终AFURLSessionManager对这些代理做了一些公共的处理,最终转发到自定义的代理AFURLSessionManagerTaskDelegate的3个代理方法中,用来负责把每个task对应的数据回调回去;
在AFURLSessionManager里边重要的代理包括NSURLSessionTaskDelegate,NSURLSessionDataDelegate以及NSURLSessionDownloadDelegate,这里重点是看源码的实现,以下是AFURLSessionManager实现的NSURLSessionDownloadDelegate代理:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { //转发代理 AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; if (self.downloadTaskDidFinishDownloading) { //调用自定义的block拿到文件存储的地址 NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (fileURL) { delegate.downloadFileURL = fileURL; NSError *error = nil; //判断从临时的下载路径移动至我们需要的路径是否成功 if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) { //不成功,发送会话下载任务未能移动文件通知 [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; } else { //成功发送会话下载任务已成功移动文件通知 [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil]; } return; } } //进行代理转发 if (delegate) { [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; } }
//周期性地通知下载进度调用bytesWritten:自上次调用该方法后,接收的数据字节数;totalBytesWritten:目前已经接收的数据字节数;totalBytesExpectedToWrite:期望收到的文件总字节数,是由Content-Length header提供。如果没有提供,默认是NSURLSessionTransferSizeUnKnown - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; if (delegate) { [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; } if (self.downloadTaskDidWriteData) { self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); } }
//当下载被取消或者失败后重新恢复下载时调用告诉代理,下载任务重新开始下载了,didResumeAtOffset:在偏移恢复 从哪里开始恢复下载的偏移 expectedTotalBytes:预期的总字节数 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; if (delegate) { [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes]; } if (self.downloadTaskDidResume) { self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); } }
这三个代理方法分别用来对下载任务进行处理,依次是下载完成时的调用,周期性通知下载进度的调用,当下载被取消或者失败后重新恢复下载时的调用;这三个代理方法最终都会进行代理转发,到AFURLSessionManagerTaskDelegate中,AF中的deleagate是需要对应每个task去私有化处理的。对应看看AFURLSessionManagerTaskDelegate中的这三个代理方法都做了什么 吧:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ //更新当前下载进度 self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite; self.downloadProgress.completedUnitCount = totalBytesWritten; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{ //更新当前下载进度 self.downloadProgress.totalUnitCount = expectedTotalBytes; self.downloadProgress.completedUnitCount = fileOffset; } //⚠️下载完成时的调用 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { self.downloadFileURL = nil; if (self.downloadTaskDidFinishDownloading) { //得到下载路径 self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (self.downloadFileURL) { NSError *fileManagerError = nil; //把下载的路径移动到我们自定义的下载路径 if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; } else { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil]; } } } }
在下载完成时的调用代理方法中,AFURLSessionManager和AFURLSessionManagerTaskDelegate中都进行了文件路径的移动,而NSURlSession代理的下载路径是所有request公用的下载路径,设置之后所有的request都会下载到之前的那个路径。而AFURLSessionManagerTaskDelegate中对应到每一个task中,每一个task可以设置自己的下载路径;
总结:这些代理方法在AFURLSessionManager中实现的时候都是对session做一个公共的处理,每一个不同的task进行特定的处理时,需要将代理转发到AFURLSessionManagerTaskDelegate中,在AFURLSessionManagerTaskDelegate的代理中实现
主要用来管理进度;并且在task结束的时候回调使用;在上述提到AFURLSessionManagerTaskDelegate中关于NSURLSessionDownloadDelegate的代理方法实现,对应到每一个task中,每一个task可以设置自己的下载路径;相应的也实现了NSURLSessionDataDelegate,NSURLSessionTaskDelegate;这些代理都是用来对当前特定的task做处理; 监听的处理方法,observeValueForKeyPath,这个方法是用来当datatask状态发生改变时的监控处理逻辑,调用block回调,用户拿到进度;
用来修改NSURLSession的resume和suspend方法,使用af_resume和af_suspend这两种方法来替换原有的resume和suspend方法;这样做是为了在方法resume或者suspend被调用时发出通知;
load方法中采用OC中Runtime的method swizzling来进行实现, AFNetworkingTaskDidResumeNotification来通知当前的任务状态为resume,那么就需要调用taskDidResume:函数,而想要调用taskDidResume:函数就得调用af_resume函数。同理,AFNetworkingTaskDidSuspendNotification来通知当前的任务状态为suspend,那么就需要调用taskDidSuspend:函数,而想要调用taskDidSuspend:函数就得调用af_suspend函数。
+ (void)load { /** WARNING: Trouble Ahead https://github.com/AFNetworking/AFNetworking/pull/2702 */ //判断当前的iOS版本是否含有NSURLSessionTask类 if (NSClassFromString(@"NSURLSessionTask")) { //创建一个session的配置对象——利用它创建一个session。进一步创建task NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnonnull" NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil]; #pragma clang diagnostic pop //originalAFResumeIMP:af_resume方法的实现(返回给指定类的实例方法)method_getImplementation:返回方法的实现 class_getInstanceMethod IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); Class currentClass = [localDataTask class]; //循环去currentClass中查看是否有resume方法 while (class_getInstanceMethod(currentClass, @selector(resume))) { Class superClass = [currentClass superclass]; IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); //判断:af_resume和当前类的父类的resume的实现不想等并且原来的af_resume和当前类的resume实现不相等 if (classResumeIMP != superclassResumeIMP && originalAFResumeIMP != classResumeIMP) { //调用swizzleResumeAndSuspendMethodForClass调剂该类的resume和suspenf方法 [self swizzleResumeAndSuspendMethodForClass:currentClass]; } currentClass = [currentClass superclass]; } [localDataTask cancel]; [session finishTasksAndInvalidate]; } } //调剂theClass的resume和suspenf方法 + (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass { Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) { af_swizzleSelector(theClass, @selector(resume), @selector(af_resume)); } if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) { af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend)); } }
AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;
来看看AFHTTPSessionManager的一些方法的实现,初始化方法最终都会调用到AFURLSessionManager中的初始化方法完成sessionManager的初始化;
当以某一种方式发送一个网络请求的时候,以GET为例:
- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary <NSString *, NSString *> *)headers progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure { //调用dataTaskWithHTTPMethod方法生成一个dataTask任务 NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:downloadProgress success:success failure:failure]; //调用resume,开始请求 [dataTask resume]; return dataTask; }
做两件事情:一是生成一个dataTask任务,二是调用resume开启请求;
该类的一个核心方法:dataTaskWithHTTPMethod,使用自定义“HTTPMethod”请求创建“NSURLSessionDataTask”
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method URLString:(NSString *)URLString parameters:(nullable id)parameters headers:(nullable NSDictionary <NSString *, NSString *> *)headers uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure { //序列化错误 NSError *serializationError = nil; //设置request相关属性&参数。(requestSerializer:请求序列化器) //1.⚠️使用请求序列化类中的requestWithMethod方法进行序列化处理。 NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; //设置请求头 for (NSString *headerField in headers.keyEnumerator) { [request setValue:headers[headerField] forHTTPHeaderField:headerField]; } //序列化失败的回调处理 if (serializationError) { if (failure) { //completionQueue如果设置了这个GCD的queue,那么从这个completionQueue回调就好;否则就从主队列回调 dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ failure(nil, serializationError); }); } return nil; } //NSURLSessionManger创建一个dataTask //2.⚠️调用dataTaskWithRequest来生成一个datatask任务 __block NSURLSessionDataTask *dataTask = nil; dataTask = [self dataTaskWithRequest:request uploadProgress:uploadProgress downloadProgress:downloadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(dataTask, error); } } else { if (success) { success(dataTask, responseObject); } } }]; return dataTask; }
该方法做了两件事情:一是对请求参数进行序列化;二是调用dataTaskWithRequest方法生成后一个datatask任务;最终返回一个datatask;
网络通讯模块所做的事情大概就这么多,在该模块中主要的任务就是发起网络请求。分成AFURLSessionManger和 AFHTTPSessionManager两部分来做处理。 AFURLSessionManger是AFHTTPSessionManager的父类,其中AFURLSessionManger内部还包含AFURLSessionManagerTaskDelegate和_AFURLSessionTaskSwizzling;AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。