总结(先说明文章分析出的一些‘认知’)
从本文中,我们可以总结出一些框架设计上的“认知”:
- 对于通用的常规配置信息方面的设计,我们可以通过定义一个“类似于NSURLSessionConfiguration、NSURLRequest”的类来完成设计;
- 对于生命周期的时机的通知,我们可以通过抽象出一个“类似于NSURLSessionDataDelegate”的协议来完成设计,此时对于框架的使用方来说,就可以根据这些“生命周期的时机的通知”来实现自己的各种各样的业务逻辑。(提升可扩展性)
- 对于一些通用且常用的处理逻辑代码,我们直接以工具类的方式来完成设计。(降低重复开发的成本)
ios系统自带的网络库
因为AFNetworking是对ios系统自带的网络库的接口(比如NSURLSession)进行封装的,因此我们先了解系统自带的网络库,然后再了解AFNetworking。
学习网络库api的最好方法就是写个demo,所以,我们先来看一个demo。
Demo1
本demo是请求https://www.baidu.com,然后打印响应的数据。
- demo代码如下:
//D2VC是个UIViewController
@interface D2VC () <NSURLSessionDataDelegate>
@property(nonatomic) NSMutableData *resultData;
@end
@implementation D2VC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 100, 390, 60)];
button.backgroundColor = UIColor.redColor;
[button addTarget:self action:@selector(get) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"get请求" forState:UIControlStateNormal];
[self.view addSubview:button];
}
- (void)get {
//创建网络请求
NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
NSURLRequest *reuqest = [NSURLRequest requestWithURL:url];
//初始化请求响应的回调方法所在的队列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 1;
//初始化NSURLSession。第3个参数必须是个串行队列
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:operationQueue];
//初始化一个请求任务
NSURLSessionDataTask *task = [session dataTaskWithRequest:reuqest];
//开始请求
[task resume];
}
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSLog(@"%s, thread=%@。该方法是在你创建session时所传入的operationQueue中被调用的,所以如果你传的是mainQueue,那么该方法就会在UI线程执行。该方法被调用时,app就已经发生并收到响应了。。。如果你在该方法不调用completionHandler,那么下面的那两个方法就不会被执行!", __func__, [NSThread currentThread]);
completionHandler(NSURLSessionResponseAllow); //调用该方法的目的是接收服务端的响应,此时下面的两个方法会被调用以便接收服务端的响应数据
self.resultData = [NSMutableData data];
}
/*
接收到server响应的数据时被调用,如果数据很多,就会被调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data {
NSLog(@"%s, thread=%@.接收到服务端响应的data。该方法是在你创建session时所传入的operationQueue中被调用的,所以如果你传的是mainQueue,那么该方法就会在UI线程执行。接下来保存该data到resultData中", __func__, [NSThread currentThread]);
[self.resultData appendData:data];
}
/*
请求完成时被回调,error表示成功或者失败
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error {
NSLog(@"%s, thread=%@", __func__, [NSThread currentThread]);
if (!error) {
NSLog(@"请求完成了,且请求成功.该方法是在你创建session时所传入的operationQueue中被调用的,所以如果你传的是mainQueue,那么该方法就会在UI线程执行。");
NSLog(@"服务端响应的数据是:%@", [[NSString alloc] initWithData:self.resultData encoding:NSUTF8StringEncoding]);
} else {
NSLog(@"请求完成了,但请求失败了.该方法是在你创建session时所传入的operationQueue中被调用的,所以如果你传的是mainQueue,那么该方法就会在UI线程执行。,error=%@", error);
}
}
@end
- 运行demo,然后点击请求按钮
- 请求结果如下图。
网络库的类关系图
根据上面demo用到的系统自带的网络相关的类及其关联的类,我们便可以梳理出目前ios的网络相关的类的关系如下图所示:
我们再结合本demo所使用的网络相关的类和协议来看看这些类或者协议所提供的功能。如下图,我们通过实现NSURLSessionDataDelegate协议来接收服务端返回的数据,并将其打印出来,使用NSURLSessionDataTask来发起“请求”,使用NSURLSession来获取请求任务(NSURLSessionDataTask),使用NSURLSessionConfiguration配置session的一些策略(比如请求超时时间),使用NSOperationQueue来让执行“NSURLSessionDataDelegate协议的实现方法”,使用NSURLRequest来配置请求策略(比如请求url)。
从本demo中,我们可以总结出,在框架的设计里面:对于通用的常规配置信息方面的设计,我们可以通过定义一个“类似于NSURLSessionConfiguration、NSURLRequest”的类来完成设计;对于生命周期的时机的通知,我们可以通过抽象出一个“类似于NSURLSessionDataDelegate”的协议来完成设计,此时对于框架的使用方来说,就可以根据这些“生命周期的时机的通知”来实现自己的各种各样的业务逻辑。
AFNetworking
Demo2
本demo是请求https://www.baidu.com,然后打印响应的数据。
- demo代码如下:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json" ,@"text/javascript", nil];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:@"https://www.baidu.com" parameters:nil headers:nil progress:^(NSProgress * _Nonnull downloadProgress) {
NSLog(@"csx,fractionCompleted=%lf", downloadProgress.fractionCompleted);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"csx,response=%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"csx,error=%@", error);
}];
- 运行代码,得到的请求结果如下图。
AFNetworking和系统网络库的类关系图
建议先看看https://www.jianshu.com/p/486b1b19cc6d文章里面的请求处理流程,然后再往下看。
根据上面demo用到的网络相关的类及其关联的类,我们便可以梳理出如下类图:
我们再结合本demo所使用的类来看看这些类所提供的功能。如下图,我们使用AFHTTPSessionManager的GET方法来发起“请求”,使用AFHTTPResponseSerializer来解析服务端返回的数据。
从本demo中,我们可以总结出:
- AFNetworking框架提供了多个将请求参数序列化的功能类,比如AFHTTPRequestSerializer类,该类针对GET请求方式的请求, 会把请求参数以键值对的方式拼接到请求url中。框架的使用方可以根据需要来选择合适的序列化功能类,如果这些功能类都不满足,那么使用方可以通过实现AFURLRequestSerialization协议来自定义自己的序列化方式。
- AFNetworking框架提供了多个将响应数据按某种格式解析的功能类,比如AFHTTPResponseSerializer类,该类会按照http协议的标准解析出响应数据中的响应头、响应体的数据。框架的使用方可以根据需要来选择合适的序列化功能类,如果这些功能类都不满足,那么使用方可以通过实现AFURLResponseSerialization协议来自定义自己的序列化方式。
- AFNetworking框架基于NSURLSession封装了‘使用NSURLSession发起请求和响应请求时的常用操作步骤’。比如,如果我们使用NSURLSession来请求某个网址,那么我们会先初始化NSURLSession,然后用该NSURLSession来获取一个NSURLSessionDataTask实例对象,接着调用NSURLSessionDataTask实例对象的resume方法,然后通过NSURLSession的delegate来获取响应数据,然后解析响应数据,最后再处理解析后的数据(具体代码请看前面的Demo1)。而如果我们使用AFNetworking来请求某个网址的话,那么我们只需要调用初始化AFHTTPSessionManager,然后调用该类的
GET:parameters:headers:progress:success:failure:
方法即可获取到解析后的响应数据,然后就可以根据业务需要来处理解析后的数据了。
我们再来看看AFURLSessionManager类的.h文件,如下图,发现该类暴露了很多系统自带的网络相关的类,比如NSURLSession、NSURLSessionDataTask、NSURLSessionConfiguration,也就是说,对于配置请求超时时间等ios系统自带的网络配置等,AFNetworking会直接暴露出来让框架的使用方来配置,而不是将这些配置封装起来。
所以,AFNetworking库带来的好处是降低了网络请求的开发成本,具体体现在:
- AFNetworking把NSURLSession的常规使用步骤封装成GET、POST等多个方法,框架使用方只需要调用某个方法即可完成请求的发起和响应数据的解析;
- AFNetworking内部实现了很多通用且常用的序列化工具类,比如AFHTTPResponseSerializer、AFHTTPRequestSerializer、AFJSONResponseSerializer,框架使用方可以直接使用这些序列化工具类来完成数据的解析。如果这些工具类都不满足框架使用方的诉求,那么框架使用方可以通过实现AFURLRequestSerialization或者AFURLResponseSerialization协议来自定义自己的解析逻辑。
所以,对于一些通用且常用的处理逻辑代码,我们直接以工具类的方式来设计。