1. 功能介绍
1.1 YTKNetwork
YTKNetwork
基于 AFNetWorking
封装的一个网络请求库,相比 AFNetworking
,YTKNetwork
提供了以下更高级的功能:
- 支持按时间缓存网络请求内容
- 支持按版本号缓存网络请求内容
- 支持统一设置服务器和
CDN
的地址 - 支持检查返回
JSON
内容的合法性 - 支持文件的断点续传
- 支持
block
和delegate
两种模式的回调方式 - 支持批量的网络请求发送,并统一设置它们的回调(实现在
YTKBatchRequest
类中) - 支持方便地设置有相互依赖的网络请求的发送,例如:发送请求A,根据请求A的结果,选择性的发送请求B和C,再根据B和C的结果,选择性的发送请求D。(实现在
YTKChainRequest
类中) - 支持网络请求
URL
的filter
,可以统一为网络请求加上一些参数,或者修改一些路径 - 定义了一套插件机制,可以很方便地为
YTKNetwork
增加功能。猿题库官方现在提供了一个插件,可以在某些网络请求发起时,在界面上显示“正在加载”的HUD
1.2 YTKNetwork
的基本使用
Step 1: 如需统一为网络请求接口加上一些参数和设置统一的服务器,需要在入口类的application didFinishLaunchingWithOptions
:中进行如下配置:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
//设置统一的服务器接口,可通过如下的baseUrl 进行设置(也可不同过详见下文)如不需要,可忽略
YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig];
config.baseUrl = @"http://api.alilo.com.cn";
config.cdnUrl = @"http://fen.bi";
// Filter 表示统一为所有的请求添加一个或多个参数(例如统一添加版本号参数) (如不需要,可忽略)
YTKUrlArgumentsFilter *urlFilter = [YTKUrlArgumentsFilter filterWithArguments:@{@"version": appVersion}];
[config addUrlFilter:urlFilter];
return YES;
}
设置好之后,所有的网络请求都会默认使用 YTKNetworkConfig
中 baseUrl
参数指定的地址。
大部分企业应用都需要对一些静态资源(例如图片、js、css)使用 CDN
。YTKNetworkConfig
的 cdnUrl
参数用于统一设置这一部分网络请求的地址。
当我们需要切换服务器地址时,只需要修改 YTKNetworkConfig
中的 baseUrl
和 cdnUrl
参数即可。
Step 2:构建请求对象继承于父类(YTKRequest
),重写父类的方法,填写参数。
例:修改请求路径需要重写父类的 requestUrl
方法
- (NSString *)requestUrl {
return @"/adidata/hotcity.html";
}
以及填写请求参数requestArgument方法
- (id)requestArgument {
return @{
@"app_installtime": @"1433937421",
@"client_id": @"qyer_ios",
@"client_secret": @"cd254439208ab658ddf9",
@"count": @"10",
@"lat": @"34.72321769638572",
@"lon": @"113.7348921712425",
@"page": @"1",
@"track_app_channel": @"App%2520Store",
@"track_app_version": @"6.3",
@"track_device_info": @"iPad%2520mini%28Wifi%29",
@"track_deviceid": @"C113A3BD-0DD9-4EB1-9BFE-ACFC1590ABBC",
@"track_os": @"ios%25207.1.2",
@"type": @"index",
@"v": @"1"
};
}
step 3: 开始请求
例:TestGETRequest:YTKRequest
TestGETRequest *test = [[TestGETRequest alloc] init];
[test startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest *request) {
NSLog(@" responseJSONObject == \n %@",request.responseJSONObject);
} failure:^(__kindof YTKBaseRequest *request) {
NSLog(@"requestOperationError == %@",request.requestOperationError);
}];
相关教程和 Demo 可参考
2. 总体设计(图文)
2.1总体结构图
上面是UIL的总体设计图,整个库分为 YTKRequest
,YTKNetworkAgent
,YTKNetworkConfig
和 YTKNetworkPrivate
四个模块。
简而言之,对于我们项目中所有的网络请求结果,当数据从 AFNetWorking
回调回来时,YTKNetworkAgent
先对它进行 JSON
的合法性的校验和版本号缓存,最后再交给最初创建的 YTKRequest
对象进行处理。
2.2UIL 中的概念
- YTKRequest: 负责网络请求根据版本号和时间进行缓存的网络请求基类,继承于 YTKBaseRequest 我们的网络请求如果需要缓存功能都需要继承于YTKRequest;
- YTKBatchRequest: 批量网络请求基类;
- YTKChainRequest: 链式请求基类;
- YTKNetworkAgent: 负责管理所有的网络请求;
- YTKNetworkConfig: 负责统一为网络请求加上一些参数,或者修改一些路径;
- YTKNetworkPrivate: 负责 JSON 数据合法性的检查,URL 的拼接以及加密等功能。
(详见4.2)
3. 流程图
YTKNewWork 网络请求库中最核心的两步:请求过程处理,以及请求到数据时的处理
图3.1
上图为 YTKNetwork 网络开始请求过程流程图:
下图为 YTKNetWork 处理返回结果流程图:
图3.2
4. 详细设计
4.1类关系UML
4.2 核心类功能介绍
目录:
[1、YTKNetwork.h](https://github.com/subvin/YTKNetworkAnalysis#4.2.1 YTKBaseRequest)
[2、YTKBaseRequest](https://github.com/subvin/YTKNetworkAnalysis#4.2.2 YTKBaseRequest )
[3、YTKRequest](https://github.com/subvin/YTKNetworkAnalysis# 4.2.3 YTKRequest )
[4、YTKNetworkConfig](https://github.com/subvin/YTKNetworkAnalysis#4.2.4 YTKNetworkConfig )
[5、YTKNetworkAgent](https://github.com/subvin/YTKNetworkAnalysis# 4.2.5 YTKNetworkAgent )
[6、YTKBatchRequest](https://github.com/subvin/YTKNetworkAnalysis# 4.2.6 YTKBatchRequest )
[7、YTKBatchRequestAgent](https://github.com/subvin/YTKNetworkAnalysis#4.2.7 YTKBatchRequestAgent )
[8、YTKChainRequest](https://github.com/subvin/YTKNetworkAnalysis#4.2.8 YTKChainRequest )
[9、YTKNetworkPrivate](https://github.com/subvin/YTKNetworkAnalysis#4.2.9 YTKNetworkPrivate )
4.2.1 YTKBaseRequest
功能: YTKNetwork 通过 YTKNetwork.h 管理其他类别,只需要在 .pch 导入 YTKNetwork.h 即可,YTKNetwork.h 代码如下:
1 |
|
4.2.2 YTKBaseRequest
功能:所有请求类的基类,持有 NSURLSessionTask 实例,responseData 等重要数据,提供了一些需要子类实现的与网络请求相关的放阿飞,处理回调的 block 和代理,命令 YTKNetworkAgent 发起网络请求。
1 |
|
发现 YTKRequest
主要是对请求数据缓存方面的处理。再看 .m 实现文件:
1 | //YTKRequest.m |
通过 start()
方法可以看出,它做的是请求之前的查询和检查工作。下面是具体实现的过程:
(1).ignoreCache
属性是用户手动设置的,如果用户强制忽略缓存,则无论是否缓存是否存在,直接发送请求。
(2)resumableDownloadPath
是断点下载路径,如果该路径不为空,说明有未完成的下载任务,则直接发送请求继续下载。
(3)loadCacheWithError
:方法验证了加载缓存是否成功的方法(方法如果返回 YES
,说明可以加载缓存,反正则不可以加载缓存)下面是 loadCacheWithError
的具体实现:
1 | - (BOOL)loadCacheWithError:(NSError * _Nullable __autoreleasing *)error { |
上面代码标红的提到缓存的元数据的,.m
也实现类元数据的获取方法:loadCacheMetadata
1 | //YTKRequest.m |
loadCacheMetadata
方法的目的是将之前被序列化保存的缓存元数据信息反序列化,赋给自身的 cacheMetadata
属性上。
现在获取了缓存的元数据并赋值给 cacheMetadata
属性上,接下来要元数据的各项信息是否符合要求:使用 validateCacheWithError
:方法进行验证。
1 | - (BOOL)validateCacheWithError:(NSError * _Nullable __autoreleasing *)error { |
如果每一项元数据信息都能通过,再在 loadCacheData
方法里面验证缓存是否能取出:
1 | - (BOOL)loadCacheData { |
如果通过了最终的考验,则说明当前请求对应的缓存是符合各项要求并可以被成功取出,也就是可以直接进行回调了。当确认缓存可以成功取出后,手动设置 dataFromCache
属性为 YES
,说明当前的请求结果是来自于缓存,而没有通过网络请求。
在这里面还有一个比较重要的方法:requestCompletePreprocessor
1 | //YTKRequest.m: |
我们可以看到, requestCompletePreprocessor
方法的任务是将响应数据保存起来,也就是做缓存。但是,缓存的保存有两个条件,一个是需要 cacheTimeInSeconds
方法返回正整数(缓存时间,单位是秒);另一个条件就是 isDataFromCache
方法返回 NO
。
进一步研究:startWithoutCache
这个方法做了哪些?
1 | //YTKRequest.m |
4.2.4 YTKNetworkConfig
功能:被 YTKRequest
和 YTKNetworkAgent
访问。负责所有请求的全局配置,对于 baseUrl
和 CDNUrl
等等。
在实际业务中,作为公司的测试需要不断的切换服务器地址,确定数据的正确性,也间接说明 YTKNetworkConfig
的必要性。
下面是以自己目前公司所用的 YTKNetwork
的 YTKNetworkConfig
的用处:
(1)首先在 AppDelegate 中:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法配置服务:
1 | - (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptinos { |
紧接着:[self setupServer];
1 | // 设置后 server |
在 IOAApiManager
实现类方法:
1 | + (void)configwork { |
我们以后就可以通过 baseUrl
进行不同的地址访问啦。大部分企业可能需要一些静态资源(例如图片,js,css 等)这就使用到了 CDN
,YTKNetworkConfig
的 cdnUrl
参数用于统一设置这一部分网络请求的地址。
YTKNetworkConfig
源码也提供了安全策略,url
过滤,缓存路径的过滤等方法如下:
1 | // 使用类方法创建单例对象 |
4.2.5 YTKNetworkAgent
功能:真正发起请求的类,负责发起请求,结束请求,并持有一个字典来存储正在执行的请求。
1、首先从请求开始:YTKNetworkAgent
把当前的请求对象添加到了自己身上并发送请求。
1 | //YTKNetworkAgent.m |
这个方法可以看出调用了 AFNetworking
的请求方法,也验证了 YTKNetwork
对 AFNetworking
高度封装。这个方法可以分成三部分:
(1).获取当前请求对应的 task
并赋值给 request
的 requestTask
属性
(2).将 request
放入专门用来保存请求的字典中,key 为 taskIdentifier
(3).启动 task
4.2.6 YTKBatchRequest
功能介绍:可以发起批量请求,持有一个数组来保存所有的请求类。在请求执行后遍历这个数组发起请求,如果其中有一个请求返回失败,则认定本组请求失败。
下面看一个初始化方法:
1 | //YTKBatchRequest.m |
初始化以后,我们就可以调用 start
方法来发起当前 YTKBatchRequest
实例所管理的所有请求了。
1 | - (void)start { |
所以在这里是遍历 YTKBatchRequest
实例的 _requestArray
并逐一发送请求。因为已经封装好了单个的请求,所以在这里直接 start
就好了。
(1)在请求之后,在每一个请求的回调的代理方法里面,来判断这次请求是否是成功的,也是在 YTKRequest
子类 YTKBatchRequest.m
判断。
1 | //YTKBatchRequest.m |
在某个请求的回调成功以后,会让成功计数+1。在+1以后,如果成功计数和当前批量请求数组里元素的个数相等,则判定当前批量请求成功,并进行当前批量请求的成功回调。
(2)失败回调
1 | //YTKBatchRequest.m |
从上面代码可以看出,如果批量请求里面有一个 request
失败了,则判定当前批量请求失败。
4.2.7 YTKBatchRequestAgent
功能简介:负责管理多个 YTKBatchRequest
实例,持有一个数组保存 YTKBatchRequest
。支持添加和删除 YTKBatchRequest
实例。
YTKBatchRequestAgent
和 YTKRequestAgent
差不多,只不过是一个和多个区别,没有多少要核心讲的,可以自己看一下 YTKBatchRequestAgent
的源码,相信都能看懂。
4.2.8 YTKChainRequest
功能简介:可以发起链式请求,持有一个数组来保存所有的请求类。当某个请求结束后才能发起下一个请求,如果其中有一个请求返回失败,则认定本请求链失败。(链式请求:例如:发送请求 A,根据请求 A 的结果,选择性的发送请求 B 和 C,再根据 B 和 C 的结果,选择性的发送请求 D。)
处理链式请求的类是 YTKChainRequest
,利用 YTKChainRequestAgent
单例来管理 YTKChainRequest
实例。
初始化方法:
1 | //YTKChainRequest.m |
YTKChainRequest
提供了添加和删除 request
的接口。
1 | //在当前 `chain` 添加 `request` 和 `callback` |
(1)链式请求发起
1 | //YTKChainRequest.m |
通过查看链式请求的实现,发现链式请求的请求队列是可以变动的,用户可以无限制地添加请求。只要请求队列里面有请求存在,则 YTKChainRequest
就会继续发送它们。
(2)链式请求的请求和回调
下面是终止方法 stop()
1 | //YTKChainRequest.m |
stop
方法是可以在外部调用的,所以用户可以随时终止当前链式请求的进行。它首先调用 clearReuqest
方法,将当前 request
停止,再将请求队列数组和 callback
数组清空。
1 | //YTKChainRequest.m |
然后在 YTKChainRequestAgent
单例里面,将自己移除掉。
4.2.9 YTKNetworkPrivate
功能简介:提供 JSON
验证,appVersion
等辅助性的方法;给 YTKBaseRequest
增加一些分类。
5.杂谈:包括优缺点、配置方式、维护更新等。
5.1、优缺点:
优点:方便更换底层网络库,方便在基类中处理缓存逻辑,以及其它一些公共逻辑,方便做对象持久化,维护更新方便
缺点:不适合于在网络请求简单的项目中使用,这样显得没有直接使用原生的网络库使用方便。
5.2、配置方式:
将 YTKNetwork
库导入工程中无需做任何配置,具体使用详见(1.2)。
5.3 维护更新
1、CocoaPods
方式:在 Podfile
文件中加入一行代码来使用 YTKNetwork
:
pod 'YTKNetwork'
2、非 CocoaPods
方式:GitHub
官网上下载最新版 YTKNetwork
库将 AFNetworking
和 YTKNetwork
库抽取出来放到项目中。