对于客户端开发来说,同网络请求相关的代码总是相对复杂的,因为它牵扯到request的序列化,response的解析,繁多的异常错误处理和多线程的调度……
很多iOS的开发人员,或许不知道NSURLConnection(现在官方已经不鼓励使用)、NSURLSession、NSURLSessionTask……但肯定知道AFNetworking(OC)与Alamofire(Swift)。在iOS端几乎所有与网络请求相关的处理,都会牵扯到这两个开源库。公司的网络层,会根据实际的业务的需要,在AFNetworking/Alamofire的基础上继续封装。
这里说一点儿题外话,之前我还不是特别理解为何要做过度封装,最近有所感受,主要原因有两点:
- 同一套底层设计,由于不同的业务需求会表现出明显的差异,因此需要在第三库的基础上,进一步封装成方便业务层代码调用的API。
- 无法保证第三库会有持续的维护,它可能会出现性能问题、bug或者版本的更迭。若自己封装过一层,那么在这些情况出现的时候,只需要对中间层进行维护,让业务层无感知。
先来想一想如果我们自己要写一个网络请求的库,它需要具备哪些功能:
- request的序列化:URL的拼接,参数解析并拼接成query,根据网络请求的方法(get、post、head……)生成正确的request
- 监听网络状况:当前的网络环境(WiFi还是cellular),网络安全状况(证书问题)
- 监听网络请求中的各个环节。例如:请求是否完成,客户端是否收到数据,是否发生错误等
- 针对与网络请求中的各个环节,要有相应的默认处理,最重要的是对各种异常错误的处理,保证到最后用户拿到的是想要的数据
- 对response的解析,各种格式JSON、XML等
针对第3点,看一看在AFNetworking是怎么实现。
这里不对代码中的一些细节作解释,若想一行一行地深究细节,推荐涂耀辉 - 简书大神写的AF解读系列。
AFNetworking是围绕着NSURLSession做了一层封装,先来看看NSURLSession的作用:
With the NSURLSession API, your app creates one or more sessions, each of which coordinates a group of related data transfer tasks. For example, if you are writing a web browser, your app might create one session per tab or window, or one session for interactive use and another session for background downloads. Within each session, your app adds a series of tasks, each of which represents a request for a specific URL (following HTTP redirects if necessary).
NSURLSession
同我们在做web开发时的session联系了起来,它是用来管理一系列的NSURLSessionTask,而我们的每一个request都会经由NSURLSession生成一个NSURLSessionTask。这样就明白了,NSURLSession就是用来管理一系列request的,至于如何管理,就不用管啦。事实上,苹果发布的NSURLSession的概念应该是从AF2.x借鉴过来的。当年的AF2.x是围绕着NSURLConnection做封装,结合NSOperation实现了网络请求的资源分配和线程调度。而如今,这一切都被内置的NSURLSession给实现了。而一个NSURLSession需要一个delegate,并实现一系列的NSURLSessionDelegate方法,这些方法就是在请求的各个环节被系统回调的,也就对应着第4个功能点。
在AF中,最核心的类是AFURLSessionManager,几乎所有同网络请求相关的核心操作都是由这个类完成或是调度完成的。我们常用的AFHTTPSessionManager是这个类的子类,子类只是封装了一下父类中的方法,其实并没有干实事。而在这个类中,就维护了一个NSURLSession类型的session。
先看看使用AF如何发出一个网络请求:
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"example.com"] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
首先需要初始化一个AFURLSessionManager,也就在这个时候,内部的初始化函数创建了一个NSURLSession,而这个session的delegate也就是维护它的manager:
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
随后,我们通过这个manager发出一个网络请求:
[manager GET:@"schema" parameters:nil progress:nil success:nil failure:nil];
在这段代码的背后,manager会通过传进来的URL和parameters序列化一个request,更进一步,manager中维护的session会为每一个request生成一个dataTask:
dataTask = [self.session dataTaskWithRequest:request];
最后只需要[dataTask resume]
就可以触发这次网络请求任务了。
那么最关键的问题就变成了manager如何管理这些dataTask了。
我个人觉得,这就是AF中最关键的地方了:manager会为每一个dataTask配置一个AFURLSessionManagerTaskDelegate。
AFURLSessionManagerTaskDelegate中只不过是将NSURLSessionDelegate中的三个方法提取了出来,提供了完整的实现,这三个方法的签名是:
- URLSession:task:didCompleteWithError:
- URLSession:dataTask:didReceiveData:
- (void)URLSession:downloadTask:didFinishDownloadingToURL:
AFURLSessionManager本可以实现NSURLSessionDelegate的全部接口,那么为什么偏偏要把这三个方法提取出来,提供另外一个代理接口?主要原因可能是这样的:每一个dataTask都是很不一样的,若对一个session管理的所有dataTask都设置成同一个delegate(即manager),那么用户如何取到不同dataTask回调回来的不同信息?因此,我们需要把那些会针对不同dataTask返回不同数据信息的回调函数单独抽出来(因为不同dataTask返回的数据信息是不一样的),为每一个dataTask都配置一个delegate,而对于一些公共环节的处理则交给manager做统一处理。
感觉干看上面这段话,挺难懂的,贴图说明:
到这里为止,manager已经实现了对每一个dataTask的监听功能。当然我的解释很简短,AF中的细节实现还有大大的可以深究的地方(大神写的代码确实牛逼),但是细节方面需要粘贴大段的代码,笔者不是提别喜欢这种方式,强烈推荐涂耀辉 - 简书大神的《AFNetworking到底做了什么》系列。
本文章后续会有增修。