YTKNetwork是基于AFNetworking网络封装,其主要的类如下:
- YTKBaseRequest
- YTKRequest
- YTKNetworkAgent
- YTKNetworkConfig
YTKNetwork的基本用法:YTKNetwork的基本思想是把每一个网络请求封装成对象,所以使用YTKNetwork,你的每个请求都需要继承YTKRequest,覆写YTKRequest的一些方法来指定特定的网络请求。
我们来分别讨论几个类的用法:
YTKNetworkAgent
网络请求的总代理,是对AFNetworking的封装,此类是一个单例,内部包含3个成员变量。
AFHTTPRequestOperationManager *_manager;
AFHTTPRequestOperationManagerd的单例网路请求manager对象
YTKNetworkConfig *_config;
负责配置一个相关的设置
NSMutableDictionary *_requestsRecord; 请求队列
对外的接口有以下方法:
-(void)addRequest:(YTKBaseRequest *)request;
// 这里详细分析一下addRequest的内部实现
- (void)addRequest:(YTKBaseRequest *)request {
YTKRequestMethod method = [request requestMethod];
NSString *url = [self buildRequestUrl:request];
id param = request.requestArgument;
AFConstructingBlock constructingBlock = [request constructingBodyBlock];
// 设置返回对象的格式,YTKRequestSerializerTypeHTTP代表返回二进制格式,YTKRequestSerializerTypeJSON代表返回一个json的根对象(NSDictionary或者NSArray)
if (request.requestSerializerType == YTKRequestSerializerTypeHTTP) {
_manager.requestSerializer = [AFHTTPRequestSerializer serializer];
} else if (request.requestSerializerType == YTKRequestSerializerTypeJSON) {
_manager.requestSerializer = [AFJSONRequestSerializer serializer];
}
_manager.requestSerializer.timeoutInterval = [request requestTimeoutInterval];
// 如果请求需要授权证书,这里设置用户名和密码
NSArray *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];
if (authorizationHeaderFieldArray != nil) {
[_manager.requestSerializer setAuthorizationHeaderFieldWithUsername:(NSString *)authorizationHeaderFieldArray.firstObject
password:(NSString *)authorizationHeaderFieldArray.lastObject];
}
// 设置其他HTTP header
NSDictionary *headerFieldValueDictionary = [request requestHeaderFieldValueDictionary];
if (headerFieldValueDictionary != nil) {
for (id httpHeaderField in headerFieldValueDictionary.allKeys) {
id value = headerFieldValueDictionary[httpHeaderField];
if ([httpHeaderField isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]]) {
[_manager.requestSerializer setValue:(NSString *)value forHTTPHeaderField:(NSString *)httpHeaderField];
} else {
YTKLog(@"Error, class of key/value in headerFieldValueDictionary should be NSString.");
}
}
}
// 如果创建了自定义的NSURLRequest对象,就使用自定的对象
NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];
if (customUrlRequest) {
// 创建 AFHTTPRequestOperation 对象
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:customUrlRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *op, id responseObject) {
// 处理结果
[self handleRequestResult:op];
} failure:^(AFHTTPRequestOperation *op, NSError *error) {
[self handleRequestResult:op];
}];
request.requestOperation = operation;
operation.responseSerializer = _manager.responseSerializer;
// 添加到请求队列
[_manager.operationQueue addOperation:operation];
} else {
// 没有自定义NSURLRequest,需要手动创建
if (method == YTKRequestMethodGet) {
// 如果需要断点续传下载文件
if (request.resumableDownloadPath) {
// 拼接参数到url
NSString *filteredUrl = [YTKNetworkPrivate urlStringWithOriginUrlString:url appendParameters:param];
NSURLRequest *requestUrl = [NSURLRequest requestWithURL:[NSURL URLWithString:filteredUrl]];
AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:requestUrl targetPath:request.resumableDownloadPath shouldResume:YES];
// 设置断点续传的进度回调block
[operation setProgressiveDownloadProgressBlock:request.resumableDownloadProgressBlock];
// 整个请求完成的回调block
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *op, id responseObject) {
[self handleRequestResult:op];
} failure:^(AFHTTPRequestOperation *op, NSError *error) {
[self handleRequestResult:op];
}];
request.requestOperation = operation;
[_manager.operationQueue addOperation:operation];
} else {
request.requestOperation = [_manager GET:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self handleRequestResult:operation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self handleRequestResult:operation];
}];
}
} else if (method == YTKRequestMethodPost) {
if (constructingBlock != nil) {
// constructingBlock是一个返回实现AFMultipartFormData协议的对象,该对象主要作用是实现文件上传
// 我们通常会上传图片或者文件需要用到multipart/form-data,实现以下即可:
/*
- (AFConstructingBlock)constructingBodyBlock {
return ^(id formData) {
NSData *data = UIImageJPEGRepresentation([UIImage imageNamed:@"currentPageDot"], 0.9);
NSString *name = @"image";
NSString *formKey = @"image";
NSString *type = @"image/jpeg";
[formData appendPartWithFileData:data name:formKey fileName:name mimeType:type];
};
}*/
request.requestOperation = [_manager POST:url parameters:param constructingBodyWithBlock:constructingBlock success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self handleRequestResult:operation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self handleRequestResult:operation];
}];
} else {
request.requestOperation = [_manager POST:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self handleRequestResult:operation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self handleRequestResult:operation];
}];
}
} else if (method == YTKRequestMethodHead) {
// 只返回head的请求
request.requestOperation = [_manager HEAD:url parameters:param success:^(AFHTTPRequestOperation *operation) {
[self handleRequestResult:operation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self handleRequestResult:operation];
}];
} else if (method == YTKRequestMethodPut) {
// 更新资源的请求
request.requestOperation = [_manager PUT:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self handleRequestResult:operation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self handleRequestResult:operation];
}];
} else if (method == YTKRequestMethodDelete) {
// 删除资源
request.requestOperation = [_manager DELETE:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self handleRequestResult:operation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self handleRequestResult:operation];
}];
} else if (method == YTKRequestMethodPatch) {
// 对PUT请求的补充,更新部分资源
request.requestOperation = [_manager PATCH:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self handleRequestResult:operation];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self handleRequestResult:operation];
}];
} else {
YTKLog(@"Error, unsupport method type");
return;
}
}
// 添加一个请求到_requestsRecord字典中,key是AFHTTPRequestOperation的hash值,value是YTKBaseRequest对象
// _requestsRecord的作用:当请求完成时,AFN返回operation,通过_requestsRecord可以反射出它所属的YTKBaseRequest对象
[self addOperation:request];
}
-(void)cancelRequest:(YTKBaseRequest *)request;
- (void)cancelRequest:(YTKBaseRequest *)request {
[request.requestOperation cancel];
[self removeOperation:request.requestOperation];
[request clearCompletionBlock];
}
-(void)cancelAllRequests;
- (void)cancelAllRequests {
NSDictionary *copyRecord = [_requestsRecord copy];
for (NSString *key in copyRecord) {
YTKBaseRequest *request = copyRecord[key];
[request stop];
}
}
-(NSString )buildRequestUrl:(YTKBaseRequest )request;
YTKBaseRequest
YTKRequest的父类,先介绍它几个重要的属性和方法。
需要子类来重写的方法
/// 请求成功的回调
-(void)requestCompleteFilter;
/// 请求失败的回调
-(void)requestFailedFilter;
/// 请求的URL
-(NSString *)requestUrl;
/// 请求的CdnURL
-(NSString *)cdnUrl;
/// 请求的BaseURL
(NSString *)baseUrl;
/// 请求的连接超时时间,默认为60秒
-(NSTimeInterval)requestTimeoutInterval;
/// 请求的参数列表
-(id)requestArgument;
/// 用于在cache结果,计算cache文件名时,忽略掉一些指定的参数
-(id)cacheFileNameFilterForRequestArgument:(id)argument;
/// Http请求的方法
-(YTKRequestMethod)requestMethod;
/// 请求的SerializerType
-(YTKRequestSerializerType)requestSerializerType;
/// 请求的Server用户名和密码
-(NSArray *)requestAuthorizationHeaderFieldArray;
/// 在HTTP报头添加的自定义参数-(NSDictionary *)requestHeaderFieldValueDictionary;
/// 构建自定义的UrlRequest,
/// 若这个方法返回非nil对象,会忽略requestUrl, requestArgument, requestMethod, requestSerializerType
-(NSURLRequest *)buildCustomUrlRequest;
/// 是否使用CDN的host地址
-(BOOL)useCDN;
/// 用于检查JSON是否合法的对象
-(id)jsonValidator;
/// 用于检查Status Code是否正常的方法
-(BOOL)statusCodeValidator;
/// 当POST的内容带有文件等富文本时使用
-(AFConstructingBlock)constructingBodyBlock;
/// 当需要断点续传时,指定续传的地址
-(NSString *)resumableDownloadPath;
/// 当需要断点续传时,获得下载进度的回调
-(AFDownloadProgressBlock)resumableDownloadProgressBlock;
里面重要的两个方法:
-(void)start {
// 调用即将开始请求的hook
[self toggleAccessoriesWillStartCallBack];
[[YTKNetworkAgent sharedInstance] addRequest:self];
}
/// remove self from request queue
-(void)stop {
// 即将结束的hook
[self toggleAccessoriesWillStopCallBack];
self.delegate = nil;
[[YTKNetworkAgent sharedInstance] cancelRequest:self];
[self toggleAccessoriesDidStopCallBack];
}
还有一个比较重要的增加hook的方法,需要自定义个对象,实现YTKRequestAccessory协议定义的一些方法来hook一些动作
-(void)addAccessory:(id)accessory {
// 因为可能有多个hook对象,所以添加到一个数组中,调用的时候也是遍历数组调用
if (!self.requestAccessories) {
self.requestAccessories = [NSMutableArray array];
}
[self.requestAccessories addObject:accessory];
}
YTKRequest
这里主要实现了一些缓存策略,重写了父类的start方法
- (void)start {
if (self.ignoreCache) {
[super start];
return;
}
// 查看缓存时间是否过期
if ([self cacheTimeInSeconds] < 0) {
[super start];
return;
}
// 查看本地的缓存版本号和当前缓存判断是否匹配
long long cacheVersionFileContent = [self cacheVersionFileContent];
if (cacheVersionFileContent != [self cacheVersion]) {
[super start];
return;
}
// 查看缓存文件是否存在
NSString *path = [self cacheFilePath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:path isDirectory:nil]) {
[super start];
return;
}
// 查看缓存时间是否过期
int seconds = [self cacheFileDuration:path];
if (seconds < 0 || seconds > [self cacheTimeInSeconds]) {
[super start];
return;
}
// 加载缓存数据
_cacheJson = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if (_cacheJson == nil) {
[super start];
return;
}
_dataFromCache = YES;
// 调用缓存结束回调
[self requestCompleteFilter];
YTKRequest *strongSelf = self;
[strongSelf.delegate requestFinished:strongSelf];
if (strongSelf.successCompletionBlock) {
strongSelf.successCompletionBlock(strongSelf);
}
[strongSelf clearCompletionBlock];
}
缓存是存放在本地文件中的,文件名用一些关键字的字符串拼接并md5来表示:
- (NSString *)cacheFileName {
NSString *requestUrl = [self requestUrl];
NSString *baseUrl = [YTKNetworkConfig sharedInstance].baseUrl;
id argument = [self cacheFileNameFilterForRequestArgument:[self requestArgument]];
NSString *requestInfo = [NSString stringWithFormat:@"Method:%ld Host:%@ Url:%@ Argument:%@ AppVersion:%@ Sensitive:%@", (long)[self requestMethod], baseUrl, requestUrl,
argument, [YTKNetworkPrivate appVersionString], [self cacheSensitiveData]];
NSString *cacheFileName = [YTKNetworkPrivate md5StringFromString:requestInfo];
return cacheFileName;
}
YTKNetworkConfig
这个类主要负责一些配置的工作,配置baseUrl,cdnUrl等工作,内部没有什么具体的实现,在其他类中获取这个类的配置信息
YTKBatchRequestAgent、YTKBatchRequest
用于方便地发送批量的网络请求,YTKBatchRequest是一个容器类,它可以放置多个 YTKRequest 子类,并统一处理这多个网络请求的成功和失败。
在如下的示例中,我们发送了4个批量的请求,并统一处理这4个请求同时成功的回调。
- (void)sendBatchRequest {
GetImageApi *a = [[GetImageApi alloc] initWithImageId:@"1.jpg"];
GetImageApi *b = [[GetImageApi alloc] initWithImageId:@"2.jpg"];
GetImageApi *c = [[GetImageApi alloc] initWithImageId:@"3.jpg"];
GetUserInfoApi *d = [[GetUserInfoApi alloc] initWithUserId:@"123"];
YTKBatchRequest *batchRequest = [[YTKBatchRequest alloc] initWithRequestArray:@[a, b, c, d]];
[batchRequest startWithCompletionBlockWithSuccess:^(YTKBatchRequest *batchRequest) {
NSLog(@"succeed");
NSArray *requests = batchRequest.requestArray;
GetImageApi *a = (GetImageApi *)requests[0];
GetImageApi *b = (GetImageApi *)requests[1];
GetImageApi *c = (GetImageApi *)requests[2];
GetUserInfoApi *user = (GetUserInfoApi *)requests[3];
// deal with requests result ...
} failure:^(YTKBatchRequest *batchRequest) {
NSLog(@"failed");
}];
}
内部实现,start方法遍历所有request,并调用start方法
- (void)start {
if (_finishedCount > 0) {
YTKLog(@"Error! Batch request has already started.");
return;
}
[[YTKBatchRequestAgent sharedInstance] addBatchRequest:self];
[self toggleAccessoriesWillStartCallBack];
for (YTKRequest * req in _requestArray) {
req.delegate = self;
[req start];
}
}
在成功回调中,有一个计数器,判断所有请求是否都已经完成
- (void)requestFinished:(YTKRequest *)request {
_finishedCount++;
if (_finishedCount == _requestArray.count) {
[self toggleAccessoriesWillStopCallBack];
if ([_delegate respondsToSelector:@selector(batchRequestFinished:)]) {
[_delegate batchRequestFinished:self];
}
if (_successCompletionBlock) {
_successCompletionBlock(self);
}
[self clearCompletionBlock];
[self toggleAccessoriesDidStopCallBack];
}
}
YTKChainRequestAgent、YTKChainRequest
用于管理有相互依赖的网络请求,它实际上最终可以用来管理多个拓扑排序后的网络请求。
以下是具体的代码示例,在示例中,我们在sendChainRequest方法中设置好了Api相互的依赖,然后。 我们就可以通过chainRequestFinished回调来处理所有网络请求都发送成功的逻辑了。如果有任何其中一个网络请求失败了,则会触发chainRequestFailed回调。
- (void)sendChainRequest {
RegisterApi *reg = [[RegisterApi alloc] initWithUsername:@"username" password:@"password"];
YTKChainRequest *chainReq = [[YTKChainRequest alloc] init];
[chainReq addRequest:reg callback:^(YTKChainRequest *chainRequest, YTKBaseRequest *baseRequest) {
RegisterApi *result = (RegisterApi *)baseRequest;
NSString *userId = [result userId];
GetUserInfoApi *api = [[GetUserInfoApi alloc] initWithUserId:userId];
[chainRequest addRequest:api callback:nil];
}];
chainReq.delegate = self;
// start to send request
[chainReq start];
}
- (void)chainRequestFinished:(YTKChainRequest *)chainRequest {
// all requests are done
}
- (void)chainRequestFailed:(YTKChainRequest *)chainRequest failedBaseRequest:(YTKBaseRequest*)request {
// some one of request is failed
}
内部实现,定义一个_nextRequestIndex,初始化为0,_requestArray请求数组,_requestCallbackArray请求回调数组
- (void)start {
if (_nextRequestIndex > 0) {
YTKLog(@"Error! Chain request has already started.");
return;
}
if ([_requestArray count] > 0) {
[self toggleAccessoriesWillStartCallBack];
[self startNextRequest];
[[YTKChainRequestAgent sharedInstance] addChainRequest:self];
} else {
YTKLog(@"Error! Chain request array is empty.");
}
}
// 顺序执行请求,_nextRequestIndex++
- (BOOL)startNextRequest {
if (_nextRequestIndex < [_requestArray count]) {
YTKBaseRequest *request = _requestArray[_nextRequestIndex];
_nextRequestIndex++;
request.delegate = self;
[request start];
return YES;
} else {
return NO;
}
}
// 请求成功回调
- (void)requestFinished:(YTKBaseRequest *)request {
// 获取当前请求的回调,并调用其回调,回调中需要用户自己去再次去add一个新的request
NSUInteger currentRequestIndex = _nextRequestIndex - 1;
ChainCallback callback = _requestCallbackArray[currentRequestIndex];
callback(self, request);
// 当不能继续执行请求时,结束本次chain请求,调用完成回调
if (![self startNextRequest]) {
[self toggleAccessoriesWillStopCallBack];
if ([_delegate respondsToSelector:@selector(chainRequestFinished:)]) {
[_delegate chainRequestFinished:self];
[[YTKChainRequestAgent sharedInstance] removeChainRequest:self];
}
[self toggleAccessoriesDidStopCallBack];
}
}
自定义YTKUrlArgumentsFilter类添加全局参数;
YTKUrlArgumentsFilter.h
#import
#import "YTKNetworkConfig.h"
#import "YTKBaseRequest.h"
/// 给url追加arguments,用于全局参数,比如AppVersion, ApiVersion等
@interface YTKUrlArgumentsFilter : NSObject
+ (YTKUrlArgumentsFilter *)filterWithArguments:(NSDictionary *)arguments;
- (NSString *)filterUrl:(NSString *)originUrl withRequest:(YTKBaseRequest *)request;
@end
YTKUrlArgumentsFilter.m
#import "YTKUrlArgumentsFilter.h"
#import "AFURLRequestSerialization.h"
@implementation YTKUrlArgumentsFilter{
NSDictionary *_arguments;
}
+ (YTKUrlArgumentsFilter *)filterWithArguments:(NSDictionary *)arguments {
return [[self alloc] initWithArguments:arguments];
}
- (id)initWithArguments:(NSDictionary *)arguments {
self = [super init];
if (self) {
_arguments = arguments;
}
return self;
}
- (NSString *)filterUrl:(NSString *)originUrl withRequest:(YTKBaseRequest *)request {
return [self urlStringWithOriginUrlString:originUrl appendParameters:_arguments];
}
- (NSString *)urlStringWithOriginUrlString:(NSString *)originUrlString appendParameters:(NSDictionary *)parameters {
NSString *paraUrlString = AFQueryStringFromParameters(parameters);
if (!(paraUrlString.length > 0)) {
return originUrlString;
}
BOOL useDummyUrl = NO;
static NSString *dummyUrl = nil;
NSURLComponents *components = [NSURLComponents componentsWithString:originUrlString];
if (!components) {
useDummyUrl = YES;
if (!dummyUrl) {
dummyUrl = @"http://www.dummy.com";
}
components = [NSURLComponents componentsWithString:dummyUrl];
}
NSString *queryString = components.query ?: @"";
NSString *newQueryString = [queryString stringByAppendingFormat:queryString.length > 0 ? @"&%@" : @"%@", paraUrlString];
components.query = newQueryString;
if (useDummyUrl) {
return [components.URL.absoluteString substringFromIndex:dummyUrl.length - 1];
} else {
return components.URL.absoluteString;
}
}
使用方法:
在AppDelegated中导入#import "YTKUrlArgumentsFilter.h"
YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig];
config.baseUrl = @"https://www.dahuobaoxian.com/member/";
// 追加全局 - begin 公共参数
NSDictionary *commonArguments = @{@"username":@"",@"password":@""};
YTKUrlArgumentsFilter *urlFilter = [YTKUrlArgumentsFilter filterWithArguments:commonArguments];
[config addUrlFilter:urlFilter];
// 追加全局 - end 公共参数