Swift的Alamofire使用久了之后,回头写OC的项目使用 -request:success:failure: 这种形式的网络请求却不太习惯了……于是动手封装一个基于AFNetworking3.1框架Alamofire风格的网络请求模块
代码github地址:ARKHTTPModule
基础功能
- 请求的创建
- 获取请求的状态过程
- 请求的操作
- 响应
这些基础功能的类为ARKRequest
1、请求的创建
通过请求地址、请求参数等创建一个网络请求实例
// 常用的HTTP请求,默认GET请求,请求超时时长15秒
+ (instancetype)requestWithURLString:(NSString *)URLStr
parameters:(nullable NSDictionary *)params;
+ (instancetype)requestWithMethod:(ARKRequestMethod)method
serializer:(ARKRequestSerializerType)serializer
URLString:(NSString *)URLStr
parameters:(nullable NSDictionary *)params
requestTimeout:(NSTimeInterval)timeout;
// 上传
+ (instancetype)uploadWithType:(ARKSessionType)type
URLString:(NSString *)URLStr
parameters:(nullable NSDictionary *)params
fromData:(nullable NSData *)bodyData;
// 下载
+ (instancetype)downloadWithType:(ARKSessionType)type
URLString:(NSString *)URLStr
parameters:(nullable NSDictionary *)params
destination:(nonnull DownloadDidFinishTargetBlock)destination;
// etc...
2、获取请求的状态过程
通过下列方法可以获取请求的进度、即将执行、即将暂停、即将取消、后台请求完毕状态
// 请求进度
- (ARKRequest *(^)(_Nullable ProgressBlock))requestProgressBlock;
// 请求即将执行
- (ARKRequest *(^)(_Nullable RequestStateBlock))requestWillResume;
// 请求即将暂停
- (ARKRequest *(^)(_Nullable RequestStateBlock))requestWillSuspend;
// 请求即将取消
- (ARKRequest *(^)(_Nullable RequestStateBlock))requestWillCancel;
// 后台请求完毕闭包
- (ARKRequest *(^)(_Nullable SessionDidFinishEventsForBackgroundURLSessionBlock))didFinishEventsForBackgroundURLSession;
3、请求的操作
对请求进行发起、暂停、取消等操作
// 发送请求
- (ARKRequest *(^)(void))resume;
// 暂停请求
- (ARKRequest *(^)(void))suspend;
// 取消请求
- (ARKRequest *(^)(void))cancel;
4、响应
这里参考了Alamofire的请求完毕回调形式
ARKRequest类中有一个NSOperationQueue串行队列,该队列在创建的时候处于暂停状态
用户在使用Response添加代码时,将Block代码放入这个队列中
当有响应时,顺序执行队列中的Block
// 返回NSData
- (ARKRequest *(^)(ResponseDataBlock))responseDataOnMainThread;
// 返回NSString
- (ARKRequest *(^)(ResponseStringBlock))responseStringOnMainThread;
// 返回JSON(NSArray或NSDictionary)
- (ARKRequest *(^)(ResponseJSONBlock))responseJSONOnMainThread;
// 返回JSON解析模型(目前默认是从JSON数据解析)
// 只要自定义的数据模型遵守< ARKMappableObject >协议
// 在返回JSON数据时,会根据传入的Class自动解析为对应的数据模型
- (ARKRequest *(^)(ResponseMappableObjectBlock, Class<ARKMappableObject>))responseMappableObjectOnMainThread;
// 返回JSON解析模型数组(目前默认是从JSON数据解析)
// 同上,解析JSON数据,返回数据模型数组
- (ARKRequest *(^)(ResponseMappableObjectArrayBlock, Class<ARKMappableObjectArray>))responseMappableObjectArrayOnMainThread;
// 解析协议
@protocol ARKMappableObject <NSObject>
+ (nullable id<ARKMappableObject>)objectWithResponseObject:(id)object;
@end
@protocol ARKMappableObjectArray <NSObject>
+ (nullable NSArray<id<ARKMappableObject>> *)arrayWithResponseObject:(id)object;
@end
请求例子
假设请求一个JSON数据
- (IBAction)startRequest:(UIButton *)sender {
// 创建请求
ARKRequest *request = [ARKRequest requestWithURLString:@"http://www.example.com/json" parameters:nil];
// 如果self不持有request,那么block内的self可以不使用弱引用,因为block执行完毕后会清空
// 相应的,执行该请求的对象(例如ViewController)生命周期将会被延长
// 如果希望退出当前控制器时,控制器可以立即销毁,那么请在block里使用weak self
request.requestWillResume(^(ARKRequest *request) {
// 请求前的准备阶段,展示加载视图
[self showLoadingView];
}).requestProgressBlock(^(NSProgress *progress) {
// 更新进度,requestProgressBlock不在主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = (float)progress.completedUnitCount / (float)progress.totalUnitCount;
});
}).responseJSONOnMainThread(^(NSURLSessionTask *task, id json, NSError *error) {
// 隐藏加载视图
[self hideLoadingView];
NSLog(@"请求完毕, thread: %@", [NSThread currentThread]);
if (error == nil) {
// 请求成功
[self requestSuccessWithJSON:json];
} else {
// 请求失败
[self requestFailureWithError:error];
}
}).resume();
}
辅助类
另外还有两个辅助类:
- ARKRequestCacheManager
- ARKNonRepetitiveRequestManager
ARKRequestCacheManager用于缓存请求
ARKNonRepetitiveRequestManager用于发起不重复的请求
1、ARKRequestCacheManager(单例)
使用ARKRequestCacheManager发起网络请求时,manager会缓存请求实例
当请求完毕后,manager移除对应的请求实例
可以通过该辅助类轻松实现暂停和取消请求
如果是使用ARKRequestCacheManager发起的下载请求,当下载失败时,manager会从error.userInfo字典中,取出NSURLSessionDownloadTaskResumeData
并且使用URL作为Key缓存在NSCache中,同时还会保存在磁盘Cache目录下
当下次发起下载请求时,会根据URL判断本地是否有断点数据
- 如果有断点数据,那么会调用 -downloadWithType:resumeData:destination: 进行断点续传
- 如果没有断点数据,重新创建下载请求
使用ARKRequestCacheManager发起网络请求
- (void)startDownload {
// 使用 ARKRequestCacheManager 发起下载请求
// ARKRequestCacheManager 会保存这次请求,直到请求完毕
// 当下载失败时,会保存这次的断点数据,下次发起时会优先读取断点数据进行断点续传
ARKRequestCacheManager *manager = [ARKRequestCacheManager sharedManager];
ARKRequest *request = [manager downloadWithType:ARKSessionTypeDefault
URLString:kURLString
parameters:nil
destination:^NSURL * _Nonnull(NSURL * _Nonnull location, NSURLResponse * _Nonnull response) {
// 指定保存路径
NSString *documentDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *path = [documentDir stringByAppendingPathComponent:@"image.jpg"];
// TODO: = =
// 如果不是fileURL,那么最后response很可能会出错
return [NSURL fileURLWithPath:path];
}];
__weak typeof(&*self) weakSelf = self;
request.requestWillResume(^(ARKRequest *request) {
// 显示加载视图
[weakSelf showLoadingView];
}).requestWillSuspend(^(ARKRequest *request) {
NSLog(@"请求即将暂停,state: %ld", request.task.state);
}).requestProgressBlock(^(NSProgress *progress) {
// 更新进度条
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.progressView.progress = (float)progress.completedUnitCount / (float)progress.totalUnitCount;
});
}).responseDataOnMainThread(^(NSURLSessionTask *task, NSData *data, NSError *error) {
// 隐藏加载视图
[weakSelf hideLoadingView];
NSLog(@"请求完毕, thread: %@", [NSThread currentThread]);
if (error != nil) {
[weakSelf showErrorMessage:[NSString stringWithFormat:@"下载失败, error:%@", error.localizedDescription]];
return ;
}
if (data == nil || [data isKindOfClass:[NSNull class]]) {
[weakSelf showErrorMessage:[NSString stringWithFormat:@"返回空数据"]];
return ;
}
weakSelf.imageView.image = [UIImage imageWithData:data];
}).resume();
}
// 暂停
- (IBAction)suspendDownload {
// 从 ARKRequestCacheManager 取出请求
ARKRequest *request = [[ARKRequestCacheManager sharedManager] requestCacheWithURLString:kURLString];
if (request != nil) {
request.suspend();
[self hideLoadingView];
}
}
// 执行
- (IBAction)resumeDownload {
// 从 ARKRequestCacheManager 取出请求
ARKRequest *request = [[ARKRequestCacheManager sharedManager] requestCacheWithURLString:kURLString];
if (request != nil && (request.state == NSURLSessionTaskStateSuspended)) {
// 继续请求
request.resume()
[self showLoadingView];
} else {
// 创建请求
[self startDownload];
}
}
2、ARKNonRepetitiveRequestManager(单例)
ARKNonRepetitiveRequestManager 同样具有 ARKRequestCacheManage r的功能,并且使用 ARKNonRepetitiveRequestManager 发起多个相同的请求时,只允许最先发起的请求生效,其余请求均忽略
使用ARKNonRepetitiveRequestManager发起网络请求
- (IBAction)backgroundDownload:(UIButton *)sender {
ARKNonRepetitiveRequestManager *manager = [ARKNonRepetitiveRequestManager sharedManager];
// 可以保证只有最先发起的请求有效,不进行重复的请求
ARKRequest *request = [manager downloadWithType:ARKSessionTypeBackground // 后台下载
URLString:kImageURLString
parameters:nil
destination:^NSURL * _Nonnull(NSURL * _Nonnull location, NSURLResponse * _Nonnull response) {
NSString *documentDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *path = [documentDir stringByAppendingPathComponent:@"BackgrounDownloadImage.jpg"];
return [NSURL fileURLWithPath:path];
}];
__weak typeof(&*self) weakSelf = self;
request.responseDataOnMainThread(^(NSURLSessionTask *task, NSData *data, NSError *error) {
NSLog(@"下载完毕, thread: %@", [NSThread currentThread]);
if (data.length > 0) {
weakSelf.imageView.image = [UIImage imageWithData:data];
}
}).didFinishEventsForBackgroundURLSession(^(NSURLSession *session) {
NSLog(@"后台下载完毕");
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
void(^completionHandler)() = delegate.didFinishEventsForBackgroundURLSession;
delegate.didFinishEventsForBackgroundURLSession = nil;
// 通知系统已接收到后台事件,清除后台网络会话标识
completionHandler();
}).resume();
}
其他
目前OC的框架中网络层仍然是 -request:success:failure:的调用形式(毕竟成熟,调用形式更普及 = =)
这个网络模块的封装纯属一时兴起,还有很多问题没有考虑,例如安全机制问题等等
总之就是有待完善
欢迎大家提出建议