网络框架选型
AFNetworking
前后端交互是我们日常开发经常遇到的情况,在iOS中最知名的网络框架应该就是AFNetworking了。使用它可以让你从服务器很轻松的得到你所需要的数据,而不需要再去使用繁琐的NURLSession,它的用法如下:
[[AFHTTPSessionManager manager] GET:@"" parameters:@{} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
}failure:^(NSURLSessionDataTask*_Nullabletask,NSError*_Nonnullerror) {
}];
我们只需要传入url和para,就可以处理成功或失败的回调了。
AFNetworking的问题
对于AFHTTPSessionManager的处理,一般是保存一个全局的单例。但是这样的话会造成一个问题:
- 修改单个请求属性会影响到全局属性的修改,如过期时间。
- 无法获取到当前请求的状态,如正在请求中、请求失败或是成功。
XPNetworking
XPNetworking是我为了解决AFNetworking的这两个问题而写的基于面向对象的网络请求框架,它不但可以方便的自定义每一个请求,还基于YYCache的封装扩展了一个缓存的功能。它的使用方法如下:
XPBaseRequest *request = [XPBaseRequest new];
request.route = @"xx";
request.path = @"xx";
request.baseUrl = @"http://www.xx.com:8080";
request.requestTimeOut = 10;
[request startCompletionBlockWithSuccess:^(XPBaseRequest * _Nonnull request) {
} failure:^(XPBaseRequest * _Nonnull request) {
}];
针对网络缓存,它提供了两种缓存方式:磁盘缓存和内存缓存,它所支持的过期模式是存储日期过后的N秒时间。设置方法如下:
request.diskCacheTime = 100;
request.cacheType = XPCacheDisk | XPCacheMemory;
request.memoryCacheTime = 100;
优点
- 面向对象,可以通过基类定制划分业务接口
- 请求对象高度自定制
- 保留请求状态,可方便进行页面数据再处理
- 集成缓存
①可方便定制缓存策略
②异步获取缓存,高并发不会阻塞主线程
③自动清理过期缓存 - 同时间相同请求只会处理一条,但返回结果都会返回
- 支持多请求结果同时处理以及多请求同步进行
- 支持上传、断点续传
实现原理
XPNetworking库的目录结构是这样的:
- XPBaseRequest,提供请求基本信息,状态的保存,对返回数据的处理
- XPChainRequest,同步请求
- XPBatchRequest,并发请求
- XPRequestNetworkManager,发起网络请求的类,用来管理XPRequestUniquePackage对象
- XPRequestUniquePackage,网络请求管理队列,用来处理同时进行多个相同请求的情况
- XPRequestSessionManager,执行网络请求的类
- XPRequestConfiguration,全局配置的类
主要从两个方面说起,一个是请求,另一个是缓存。
缓存
之所以将缓存封装进网络请求框架是因为iOS许多页面都是需要缓存一些数据,以便用户在进入app可以看到页面上的内容,而不是空白页面,优化用户体验。在之前的做法中,缓存与网络请求是分开的,在请求之前先取数据,然后再去缓存,存储的数据错综复杂,很有可能处理逻辑与从网络请求获取的数据处理逻辑不相同,这样就造成了代码的冗余,以及许多不必要的麻烦。
这里的缓存框架用的是YYCache,他是一个基于sqlite的高效缓存框架。它可以让我们不需要操作数据库就可以达到缓存数据的效果。但在使用过程中我发现有两点问题:
- 它的缓存模式并不是我需要的,它的缓存过期时间是基于使用时间,而我需要的是基于缓存时间。
- 它开启了wal,会在一定的情况下造成app占用内存过高。
所以针对这两个问题,我封装了一个管理YYCache的工具:
XPCache。
它的API基本与YYCache相同。它实现缓存过期机制实际上就是在YYCache中预留一个字段用来保存其他数据的缓存设置:
NSString *cacheFolder = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
NSString *path = [cacheFolder stringByAppendingPathComponent:XPCachePathName];
_diskCache = [[YYDiskCache alloc] initWithPath:path];
_memoryCache = [YYMemoryCache new];
_memoryCache.name = XPCachePathName;
_cacheManager = [XPCacheManager new];
id data = [_diskCache objectForKey:XPCacheManagerName];
if ([data isKindOfClass:[NSDictionary class]]) {
_cacheManager.diskData = [NSMutableDictionary dictionaryWithDictionary:data];
}else{
[_diskCache removeAllObjects];
}
_cacheManager.delegate = self;
[_cacheManager start];
关于频繁调用造成内存过大的问题是通过在操作YYCache时,先进行自身一套机制处理,然后再去操作YYCache。这样就减少了YYCache的高频操作。
网络请求
BaseRequest
网络请求是基于面向对象的,在这里有一个BaseRequest,它包含的是一个网络请求最基本的一些元素:
一、发起请求
/**
请求host
*/
@property (nonatomic, copy) NSString * baseUrl;
/**
首路径
拼接: baseUrl + route + path
*/
@property (nonatomic, copy) NSString * route;
/**
路径
*/
@property (nonatomic, copy) NSString * path;
/**
参数
*/
@property (nonatomic, strong) NSMutableDictionary * params;
/**
请求方法
*/
@property (nonatomic, assign) XPRequestMethod method;
在发起请求后,会根据设置的request来设置请求头和响应,在没有设置request的属性时,所使用的是全局属性。如下
- (AFHTTPRequestSerializer *)configRequestSerializerByRequset:(XPBaseRequest *)request{
AFHTTPRequestSerializer * serializer = [self requestSerializerWithRequestSerializerType:request.requestSerializerType];
NSArray *requestAuthorizationHeaderFieldArray = request.requestAuthorizationHeaderFieldArray;
if (request.requestAuthorizationHeaderFieldArray) {
[serializer setAuthorizationHeaderFieldWithUsername:[requestAuthorizationHeaderFieldArray firstObject] password:[requestAuthorizationHeaderFieldArray lastObject]];
}
serializer.allowsCellularAccess = request.allowsCellularAccess;
NSDictionary *headerFieldValueDictionary = request.headerFieldValueDictionary?request.headerFieldValueDictionary:self.configuration.headerFieldValueDictionary;
if (headerFieldValueDictionary) {
for (NSString * httpHeaderField in headerFieldValueDictionary.allKeys) {
NSString *value = [request.headerFieldValueDictionary objectForKey:httpHeaderField];
[serializer setValue:value forHTTPHeaderField:httpHeaderField];
}
}
serializer.timeoutInterval = request.requestTimeOut == 0?self.configuration.timeOut:request.requestTimeOut;
self.manager.requestSerializer = serializer;
if (request.securityPolicy) {
self.manager.securityPolicy = request.securityPolicy;
}else{
self.manager.securityPolicy = self.configuration.securityPolicy;
}
return serializer;
}
- (AFHTTPResponseSerializer *)confiResponseSerializerByRequest:(XPBaseRequest *)request{
XPResponseSerializerType type = request.responseSerializerType;
switch (type) {
case XPResponseSerializerTypeHTTP:
return self.httpResponseSerializer;
break;
case XPResponseSerializerTypeJSON:{
NSSet *set = [request.acceptableContentTypes allObjects].count > 0?request.acceptableContentTypes : self.acceptableContentTypes;
self.jsonResponseSerializer.acceptableContentTypes = set;
return self.jsonResponseSerializer;
break;
}
case XPResponseSerializerTypeXMLParser:{
NSSet *set = [request.acceptableContentTypes allObjects].count > 0?request.acceptableContentTypes : self.acceptableContentTypes;
self.xmlResponseSerializer.acceptableContentTypes = set;
return self.xmlResponseSerializer;
break;
}
default:
break;
}
}
二、请求状态
然后用这个请求头发起请求。同时在发起请求的时候给定请求状态,一共有这几种:
typedef NS_ENUM(NSInteger, XPRequestRunningStatus) {
XPRequestRunningStatus_unStart = 0,//未开始
XPRequestRunningStatus_running,//请求中-单
XPRequestRunningStatus_finish_Success,//请求成功
XPRequestRunningStatus_finish_Failure,//请求失败
XPRequestRunningStatus_finish_Cancel,
};
这是request对象所包含的状态属性:
/**
运行状态
*/
@property (nonatomic, assign, readonly) XPRequestRunningStatus runningStaus;
/**
是否取消
*/
@property (nonatomic, assign, readonly) BOOL isCancelled;
/**
是否运行中
*/
@property (nonatomic, assign, readonly) BOOL isExecuting;
/**
是否已经结束
*/
@property (nonatomic, assign, readonly) BOOL isFinshed;
@property (nonatomic, assign, readonly) BOOL isUnstart;
/**
是否是请求失败
*/
@property (nonatomic, assign, readonly) BOOL isFail;
三、同一请求同时发起时的合并
XPRequestUniquePackage类主要用来处理请求合并,当一个请求在发起到完成这段时间如果又发起一个相同的请求时,这个时候其实是没必要再进行网络请求的,这个时候可以把不需要发起的请求放入到队列中,在上一个请求完成后同时处理。
四、request对象自定制返回规则
相同的请求在处理时有可能会处理逻辑的测重点可能不同,request提供了处理自身返回结果的预处理能力。
/**
json数据是否合法
*/
- (BOOL)validateJson;
在网络请求回调时会调用这个方法,在处理后返回YES则证明结果与预期相符,如果为NO则证明失败,会进入失败的回调。
五、请求的异步和同步
XPBatchRequest和XPChainRequest在内部处理了多请求的同步和异步。使用时只需要将请求加入到队列中,即可在回调中处理。
XPBatchRequest *batch = [[XPBatchRequest alloc] initWithRequest:@[request]];
[batch startCompletionBlockWithSuccess:^(XPBatchRequest *batchRequest) {
}];
可在连获取到成功和失败的请求。
总结
面向对象的网络编程,可以让我们在与后端的交互中脉络更加清晰,减少资源的浪费,从而使我们的开发更加的高效。
感谢YTKNetwork提供的思路!
网友评论