自从AF使用NSURLSession重写之后,还没有看过源码,NSURLSession是基于NSURLConnection的封装,应该是汲取了AF之前的设计,内部封装了一个NSOperation来实现异步请求。最近要做一个音频播放的边播边缓存的功能,网路请求这块需要封装一下,随便研究一下AF的源码,这里我按模块逐个分析一下。
AFURLSessionManager
这个模块是AF中最大也是最重要的一个模块,它封装了一个session,提供了一系列的初始化方法和创建task的方法。如果大家对session和task的概念还不太理解,可以先看一下NSURLSession这个类的相关内容(这个大概说一下,session是管理所有网络请求的,每一个网络请求是一个task,task是使用session创建的,所有task共享session的相关设置)。
先来看一下创建task的方法,这里总共有datatask,uploadtask和downloadtask三种task,不过他们的创建逻辑都是一样的,以datatask为例
|
|
先看看上面这个宏的实现
|
|
这里这样做主要是为了解决ios8之前的一个bug,如果异步同时创建task的话,task的id有可能是一样的,这会导致回调时出现问题(completionHandler被替换),为了解决这个问题,创建task的操作都放在一个串行队列中执行。
然后看一下添加代理这个又是要干嘛
|
|
可以看到,af为每一个task都创建了一个delegate,然后将代理存在了self.mutableTaskDelegatesKeyedByTaskIdentifier这个字典中,键值是task的id,这里也解释了上面的为啥要同步创建task,那么它为啥要为每个task添加一个代理呢,我们继续往下看。
看完task的创建,然后我们再来看一下数据的回调。af为每个NSURLSession的回调函数创建了对应block和setBlock的方法,如果你需要哪个代理里面的数据,直接创建对应的block即可。
我们先来看最常用的回调方法,即请求完成的回调
|
|
咋一看有点奇怪,这2方法名一毛一样啊。没有错,这两个方法是同一个代理方法,只是他们的实现对象是不一样的,第一个的实现对象是AFURLSessionManager类,第二个的实现对象则是AFURLSessionManagerTaskDelegate这个类,这里就解释上面的为啥要为每一个task设置一个代理,af这里其实是吧NSURLSession的代理又包了一层,将NSURLSession的数据都传到AFURLSessionManagerTaskDelegate这个类里面去处理了。
然后我们看一下这个回调中对数据的处理,可以看到回调的返回都是放在一个gcd的group中处理的,如果我们设置了返回处理线程的话就使用设置的线程,否则会在主线程中处理数据的返回,如果有错误是直接返回了,没有错误的话,会先使用responeseSerializer去验证一下返回的数据格式是否合法,这个类我们下面在细看。
继续看其他几个我觉得比较常用的回调
|
|
这2个回调一个是收到responese被触发,这个回调可以拿到相应头部信息,比如接收文件的大小,格式等,一个是收到数据时被触发,进度相关的信息就在通过这个回调拿到的。还有一点就是,download的task不会走datatask的代理,意味着download的数据只能在下载完成后通过文件的方式拿到。
AFHTTPSessionManager
这个类就是继承了AFURLSessionManager,然后提供了一些封装好的创建task的方法。平时二次封装也是使用这个类就行。
然后这个类有三个比较重要的属性
|
|
这里简单介绍每个属性的用途,下面还有详解。
requestSerializer主要用来设置httpheader,responseSerializer主要用来解析返回数据是否合法,
securityPolicy主要用来验证服务器证书。
AFHTTPRequestSerializer
先来看一下AFHTTPRequestSerializer在AFHTTPSessionManager中是如何使用的
|
|
可以看到,在调用AFURLSessionManager的创建task方法前,会先调用一下下面这个方法生成一个request
|
|
那我们来具体看一下这个方法的实现
|
|
具体流程:
- 使用url和method创建一个mutableRequest,
- kvo相关属性,如果不为空则kvc到mutableRequest
- 设置HTTPRequestHeaders数组中相关属性到mutableRequest
- 将传入的参数做一下url编码
- 如果是get,head,delete方式,将参数加到url后面,否则,将参数设置为mutableRequest的body,同时设置mutableRequest的Content-Type.
可以看出,这个类的主要作用就是创建request,给request设置相应的header和body,对url和参数进行编码(url使用URLEncode,body使用UNICode编码).
AFHTTPResponseSerializer
这个类在上面的complete回调中有提到,这里具体分析。
af一共提供了7种ResponseSerializer,分别是:
|
|
不同的response针对不同的数据类型。
他们的主要区别其实就在acceptableContentTypes这个属性上,它是一个集合,用来存储我们需要接受的数据类型。
比如JSON的acceptableContentTypes为:
|
|
IMAGE的acceptableContentTypes为:
|
|
我们可以通过NSHTTPSessionManager的responseSerializer修改你需要的数据类型。
回到之前的complete回调,看看具体怎么使用的
|
|
[response MIMEType] 这个type就是当前服务器返回的数据的格式,需要self.acceptableContentTypes这个集合中包含上面的type,解析数据的时候才会返回成功,所以,如果遇到解析失败的话,可以断点到这里看一下这个type然后手动添加到acceptableContentTypes这个集合中。
AFSecurityPolicy
af提供了三种验证证书的方式,第一种不需要本地证书。
|
|
对应的有几种不同的初始化方法
|
|
然后我们看一下到底是如何进行证书验证的
|
|
这里重点看一下self.allowInvalidCertificates这个参数,可以看到,如果使用默认的验证策略,同时self.allowInvalidCertificates = YES的话,会直接返回YES,相当于没有验证,所有这里我们一般将self.allowInvalidCertificates = NO。self.validatesDomainName这个参数默认为YES就可以了。
验证服务器证书是否合法的方法为AFServerTrustIsValid,这个方法里面就是调用的系统的方法。
那这个验证方法到底在哪里调用的呢,我们看一下下面这个代理,在NSURLSessionManager中
|
|
所有的https请求都会先进这个代理,这个challenge.protectionSpace对象中就是服务器发过来的需要客户端验证的证书,域名等相关重要信息,然后af调用evaluateServerTrust这个方法对相关信息进行验证。
实战,简单的二次封装
我这边使用一个单例来管理AFHTTPSessionManager,在init方法中,设置相关delegate对应的block.这里设置block主要是满足我需要在开始获取到音频数据的时候就对数据进行处理,不需要等到整个请求完成再处理数据的需求。如果需要其他delegate的信息,添加相应的block即可。
|
|
因为我这边需要请求音视频的数据,af提供的几种responese默认是不支持音视频格式的,所有我封装了几种文件type,每种type对应不同的responese
|
|
然后提供了2个block,分别是didReceiveResponseBlock和didReceiveDataBlock,如果需要拿到请求过程中的相关数据和信息,直接实现这2个block就ok了.
|
|
针对不同的数据类型,我这边提供了相应的task的创建方法,然后还提供了一个总的创建task的方法和一个下载方法
|
|
实现部分就比较简单了
|
|