AFNetworking源码简析
2017-07-21 00:00
197 查看
AFNetworking基本是苹果开发中网络请求库的标配,它是一个轻量级的网络库,专门针对
iOS和
OS X的网络应用设计,具有模块化的架构和丰富的
APIs接口,功能强大并且使用简单,深受苹果应用开发人员的喜爱。
本文主要介绍一下
AFNetworking(版本:
3.1.0)的模块结构、请求的执行过程、网络状态监测以及网络安全的处理等等,从而对
AFNetworking的具体功能、执行过程有一个大致的了解,在实际的项目开发过程中,能够更好的进行应用。
一、结构
下面是AFNetworking的源码结构图,主要分为:
NSURLSession核心代码、
Reachability、
Security、
Serialization和
UIKit等
5部分。
AFNetworking的源码结构图:
<Center>
![图1](http://upload-images.jianshu.io/upload_images/1843940-b101b8580f9b3b6c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>
AFHTTPSessionManager的依赖关系:
<Center>
![图2](http://upload-images.jianshu.io/upload_images/1843940-7674758339ffdbfc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>
AFNetworking的网络数据的序列化(
Serialization)的类结构图:
<Center>
![图3](http://upload-images.jianshu.io/upload_images/1843940-85ce5d8b48246783.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>
NSURLSessionTask的类结构图:
<Center>
![图4](http://upload-images.jianshu.io/upload_images/1843940-d96b1405c0b74de2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>
各部分功能介绍如下:
1.NSURLSession
这是网络请求的核心代码,承担发送请求、接收数据、异常处理等功能,以及请求和响应的数据序列化功能2.Reachability
用来监控设备的网络状态,只能识别WWAN和
WiFi这两种网络
3.Security
网络安全的设计,尤其是利用https增强数据安全性4.Serialization
请求和响应数据的序列化处理5.UIKit
主要是对一些常用UI控件做网络交互方便的扩展二、应用
1.网络状态监测
网络状态监测,通过AFNetworkReachabilityManager的单例对象设置网络状态变化的
block,然后通过
AFStringFromNetworkReachabilityStatus把当前网络状态转换为对应的字符串。一般情况下,设置网络监测的代码放到
AppDelegate.m里,监测整个项目的网络状态变化。
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status)); }]; [[AFNetworkReachabilityManager sharedManager] startMonitoring];
2.网络安全
HTTPS相比
HTTP提高了通信的安全性,而苹果也建议网络请求使用
HTTPS,所以有必要了解一下
HTTPS的工作过程(HTTPS通信流程介绍)。而
AFNetworking中的
AFSecurityPolicy类用来验证证书是否有效,从而防止被中间人攻击。在实际开发中,会在
AFURLSessionManager类的初始化方法里使用默认的安全设置:
不允许无效或过期的证书
验证domain名称
不对证书和公钥进行验证
// AFURLSessionManager.m - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { ... self.securityPolicy = [AFSecurityPolicy defaultPolicy]; ... } // AFSecurityPolicy.m + (instancetype)defaultPolicy { AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = AFSSLPinningModeNone; return securityPolicy; }
部分重要属性介绍如下:
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
a、AFSSLPinningModeNone:不验证服务器的整数,完全信任
b、AFSSLPinningModePublicKey:验证服务器返回的证书中的PublicKey
c、AFSSLPinningModeCertificate:把服务器的返回的证书和本地证书进行验证
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
这个属性保存着所有的可用做校验的证书的集合
@property (nonatomic, assign) BOOL allowInvalidCertificates;
是否允许无效或过期的证书,默认是不允许
@property (nonatomic, assign) BOOL validatesDomainName;
是否验证证书中的域名domain,默认是验证
3.HTTP请求
HTTP请求在开发中常用的就是
GET、
POST这两种类型,针对这两种请求方式的区别请参考HTTP协议格式详解。本文从源码实现的角度,不会详细介绍各个类的功能及依赖关系,只是面向应用的角度来分析一下这两种请求如何实现的:
整个网络协议的流程图:
GET/POST
AFNetworking发起的网络请求,
GET和
POST的差异就是在设置请求的
URL和
HTTPBody时的差异。下面根据上面的流程图详细介绍一下具体的代码实现:
步骤一:发起网络请求
通过
requestSerializer.timeoutInterval设置请求超时时间
通过
responseSerializer.acceptableContentTypes设置客户端可接收的数据类型
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.requestSerializer.timeoutInterval = 10; manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil]; [manager GET:@"https://app.chaoaicai.com/api/todayApi/discoveryInfo.app" parameters:@{@"name": @"kelvin"} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"%@", responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@", error); }];
步骤二、三:创建NSMutableURLRequest 和 NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress success:(void (^)(NSURLSessionDataTask *, id))success failure:(void (^)(NSURLSessionDataTask *, NSError *))failure { ... // 创建Request NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; ... // 创建dataTask dataTask = [self dataTaskWithRequest:request uploadProgress:uploadProgress downloadProgress:downloadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { ... }]; ... }
步骤四:设置AFURLSessionManager的代理
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler { __block NSURLSessionDataTask *dataTask = nil; // 创建dataTask url_session_manager_create_task_safely(^{ dataTask = [self.session dataTaskWithRequest:request]; }); // 设置dataTask的代理 [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; return dataTask; }
步骤五:调用NSURLSessionDataTask的resume方法开始网络请求
通过调用
NSURLSessionTask的
resume来启动任务,也可以调用
suspend来挂起任务
- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters progress:(void (^)(NSProgress * _Nonnull))downloadProgress success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure { ... [dataTask resume]; ... }
步骤六:AFURLSessionManagerTaskDelegat的方法处理回调数据
这个方法里主要功能是处理接收到的数据,调用保存的任务完成的
block,并发送接收完成的通知
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" __strong AFURLSessionManager *manager = self.manager; __block id responseObject = nil; __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; ... if(error){ ... } else { ... } }
步骤7:清理NSURLSessionDataTask配置
删除
NSURLSessionDataTask的配置信息,如:删除监控收发数据进度的通知、任务开启或挂起的通知、从
mutableTaskDelegatesKeyedByTaskIdentifier中删除任务的
delegate等等。之后,回调用户设置的
block(成功或者失败的回调),等
block执行结束后,就结束这个
GET请求。
- (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 if (delegate) { // 调用代理方法处理接收到的数据 [delegate URLSession:session task:task didCompleteWithError:error]; // 清理dataTask的配置信息 [self removeDelegateForTask:task]; } if (self.taskDidComplete) { // 回调用户的block self.taskDidComplete(session, task, error); } } // 清理dataTask配置 - (void)removeDelegateForTask:(NSURLSessionTask *)task { NSParameterAssert(task); AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; [self.lock lock]; [delegate cleanUpProgressForTask:task]; [self removeNotificationObserverForTask:task]; [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; [self.lock unlock]; }
下面介绍一下
GET和
POST在请求时,封装
URL和
HTTPBody的差别:
GET请求
把
parameters字典转换为
key=value&key=value的形式,并附加在用户设置的
url的后面作为请求的
url
POST请求
把
parameters字典转换为
key=value&key=value的形式,然后添加到
HTTPBody里面作为请求体发送
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error { // 添加用户配置的header的key-value值 [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }]; ... if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { // GET 请求 if (query && query.length > 0) { mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; } } else { // #2864: an empty string is a valid x-www-form-urlencoded payload // POST 请求 if (!query) { query = @""; } if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; } [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; } return mutableRequest; }
4.普通请求(直接用NSURLSession)
AFNetworking除了直接用
GET或
POST发送请求外,还可以直接用
AFURLSessionManager发送网络请求,而
GET或
POST内部其实也是调用的
AFURLSessionManager,这样我们也可以手动设置任务的开启或挂起。
下面是
AFNetworking的
Github的示例代码,关于上传或下载代码,请参考AFNetworking的介绍
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; NSURL *URL = [NSURL URLWithString:@"http://fishbay.cn"]; NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { if (error) { NSLog(@"Error: %@", error); } else { NSLog(@"%@ %@", response, responseObject); } }]; [dataTask resume];
至此,AFNetworking的分析到此结束,本文只是从应用的角度分析了一下源码的实现,如有不足之处,欢迎指正。(本文部分图片来自互联网,版权归原作者所有)
参考资料AFNetworking库下载地址
AFNetworking3.0源码解析1
AFNetworking3.0源码解析2
iOS网络框架-AFNetworking3.1.0源码解读
AFNetworking源码阅读系列
AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy
相关文章推荐
- AFNetworking源码简析
- novaclient中的源码简析
- quartz源码简析(三)
- 转载AFNetworking源码解析(三)
- AFNetworking (3.1.0) 源码解析 <四>
- AFNetworking-源码解析
- ArrayList 概述 与源码简析
- unity UGUI部分源码简析
- uboot 源码分析(2)uboot 环境变量实现简析
- AFNetworking源码解析(一)
- OpenStack之Glance源码简析
- lucene搜索源码过程简析
- AFNetworking源码之Serialization模块
- ffmpeg源码简析 结构总览
- 转载AFNetworking源码解析(四)
- AFNetworking 3.0 源码解读 总结(干货)(上)
- 模拟AndroidPN Client(1)——AndroidPN源码简析
- AFNetworking (3.1.0) 源码解析 <六>
- AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization
- AFNetworking 3.0 源码解读(六)之 AFHTTPSessionManager