一鲤遥、什么是YTKNetwork
YTKNetwork是一個(gè)基于AFNetworking的網(wǎng)絡(luò)層封裝验烧。
二虐拓、包括那幾個(gè)類
○? YTKBaseRequest
○? YTKRequest
○? YTKNetworkAgent
○? YTKNetworkConfig
△YTKNetwork的基本用法:基本用法沼沈,YTKNetwork 的基本的思想是把每一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝成對(duì)象丈冬。所以使用 YTKNetwork尸折,你的每一種請(qǐng)求都需要繼承 YTKRequest類,通過覆蓋父類的一些方法來構(gòu)造指定的網(wǎng)絡(luò)請(qǐng)求殷蛇。
下面會(huì)分別講解這幾個(gè)類。
YTKNetworkAgent
網(wǎng)絡(luò)請(qǐng)求的總代理類橄浓,是對(duì)AFNetworking的封裝粒梦。此類是一個(gè)單例。
內(nèi)部包含的三個(gè)成員變量:
AFHTTPRequestOperationManager *_manager;
AFHTTPRequestOperationManagerd的單例網(wǎng)路請(qǐng)求manager對(duì)象
YTKNetworkConfig *_config;
負(fù)責(zé)配置一個(gè)相關(guān)的設(shè)置
NSMutableDictionary *_requestsRecord;
請(qǐng)求隊(duì)列
對(duì)外的接口有以下方法:
-(void)addRequest:(YTKBaseRequest *)request;
// 這里詳細(xì)分析一下addRequest的內(nèi)部實(shí)現(xiàn)
- (void)addRequest:(YTKBaseRequest *)request {
YTKRequestMethod method = [request requestMethod];
NSString *url = [self buildRequestUrl:request];
id param = request.requestArgument;
AFConstructingBlock constructingBlock = [request constructingBodyBlock];
// 設(shè)置返回對(duì)象的格式荸实,YTKRequestSerializerTypeHTTP代表返回二進(jìn)制格式匀们,YTKRequestSerializerTypeJSON代表返回一個(gè)json的根對(duì)象(NSDictionary或者NSArray)
if (request.requestSerializerType == YTKRequestSerializerTypeHTTP) {
_manager.requestSerializer = [AFHTTPRequestSerializer serializer];
} else if (request.requestSerializerType == YTKRequestSerializerTypeJSON) {
_manager.requestSerializer = [AFJSONRequestSerializer serializer];
}
_manager.requestSerializer.timeoutInterval = [request requestTimeoutInterval];
// 如果請(qǐng)求需要授權(quán)證書,這里設(shè)置用戶名和密碼
NSArray *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];
if (authorizationHeaderFieldArray != nil) {
[_manager.requestSerializer setAuthorizationHeaderFieldWithUsername:(NSString *)authorizationHeaderFieldArray.firstObject
password:(NSString *)authorizationHeaderFieldArray.lastObject];
}
// 設(shè)置其他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.");
}}}
// 如果創(chuàng)建了自定義的NSURLRequest對(duì)象准给,就使用自定的對(duì)象
NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];
if (customUrlRequest) {
// 創(chuàng)建 AFHTTPRequestOperation 對(duì)象
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:customUrlRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *op, id responseObject) {
// 處理結(jié)果
[self handleRequestResult:op];
} failure:^(AFHTTPRequestOperation *op, NSError *error) {
[self handleRequestResult:op];
}];
request.requestOperation = operation;
operation.responseSerializer = _manager.responseSerializer;
// 添加到請(qǐng)求隊(duì)列
[_manager.operationQueue addOperation:operation];
} else {
// 沒有自定義NSURLRequest泄朴,需要手動(dòng)創(chuàng)建
if (method == YTKRequestMethodGet) {
// 如果需要斷點(diǎn)續(xù)傳下載文件
if (request.resumableDownloadPath) {
// 拼接參數(shù)到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];
// 設(shè)置斷點(diǎn)續(xù)傳的進(jìn)度回調(diào)block
[operation setProgressiveDownloadProgressBlock:request.resumableDownloadProgressBlock];
// 整個(gè)請(qǐng)求完成的回調(diào)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是一個(gè)返回實(shí)現(xiàn)AFMultipartFormData協(xié)議的對(duì)象,該對(duì)象主要作用是實(shí)現(xiàn)文件上傳
// 我們通常會(huì)上傳圖片或者文件需要用到multipart/form-data露氮,實(shí)現(xiàn)以下即可:? ? ? ? ? ? ? ? /*? ? ? ? ? ? ? ? ? - (AFConstructingBlock)constructingBodyBlock {? ? ? ? ? ? ? ? ? return ^(idformData) {
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的請(qǐng)求
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) {
// 更新資源的請(qǐng)求
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) {
// 對(duì)PUT請(qǐng)求的補(bǔ)充祖灰,更新部分資源
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;}}
// 添加一個(gè)請(qǐng)求到_requestsRecord字典中,key是AFHTTPRequestOperation的hash值畔规,value是YTKBaseRequest對(duì)象
// _requestsRecord的作用:當(dāng)請(qǐng)求完成時(shí)局扶,AFN返回operation,通過_requestsRecord可以反射出它所屬的YTKBaseRequest對(duì)象
[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的父類叁扫,先介紹它幾個(gè)重要的屬性和方法三妈。
需要子類來重寫的方法
/// 請(qǐng)求成功的回調(diào)
-(void)requestCompleteFilter;
/// 請(qǐng)求失敗的回調(diào)
-(void)requestFailedFilter;
/// 請(qǐng)求的URL
-(NSString *)requestUrl;
/// 請(qǐng)求的CdnURL
-(NSString *)cdnUrl;
/// 請(qǐng)求的BaseURL
(NSString *)baseUrl;
/// 請(qǐng)求的連接超時(shí)時(shí)間,默認(rèn)為60秒
-(NSTimeInterval)requestTimeoutInterval;
/// 請(qǐng)求的參數(shù)列表
-(id)requestArgument;
/// 用于在cache結(jié)果莫绣,計(jì)算cache文件名時(shí)畴蒲,忽略掉一些指定的參數(shù)
-(id)cacheFileNameFilterForRequestArgument:(id)argument;
/// Http請(qǐng)求的方法
-(YTKRequestMethod)requestMethod;
/// 請(qǐng)求的SerializerType
-(YTKRequestSerializerType)requestSerializerType;
/// 請(qǐng)求的Server用戶名和密碼
-(NSArray *)requestAuthorizationHeaderFieldArray;
/// 在HTTP報(bào)頭添加的自定義參數(shù)? ??
? -(NSDictionary *)requestHeaderFieldValueDictionary;
? ? /// 構(gòu)建自定義的UrlRequest,
? /// 若這個(gè)方法返回非nil對(duì)象对室,會(huì)忽略requestUrl, requestArgument, requestMethod, requestSerializerType? ? ? ? -(NSURLRequest *)buildCustomUrlRequest;??
/// 是否使用CDN的host地址? ? ? ? -(BOOL)useCDN;? ? /// 用于檢查JSON是否合法的對(duì)象? ? ? ? -(id)jsonValidator;??
/// 用于檢查Status Code是否正常的方法? ? ? ? -(BOOL)statusCodeValidator;? ? /// 當(dāng)POST的內(nèi)容帶有文件等富文本時(shí)使用? ? ? ? -(AFConstructingBlock)constructingBodyBlock;?
? /// 當(dāng)需要斷點(diǎn)續(xù)傳時(shí)模燥,指定續(xù)傳的地址? ? ? ? -(NSString*)resumableDownloadPath;?
? /// 當(dāng)需要斷點(diǎn)續(xù)傳時(shí)咖祭,獲得下載進(jìn)度的回調(diào)? ? ? ? -(AFDownloadProgressBlock)resumableDownloadProgressBlock;里面重要的兩個(gè)方法:- (void)start {? ?
// 調(diào)用即將開始請(qǐng)求的hook??
[self toggleAccessoriesWillStartCallBack];? ? [[YTKNetworkAgent sharedInstance] addRequest:self];}
/// remove self from request queue- (void)stop {?
? // 即將結(jié)束的hook?
? [self toggleAccessoriesWillStopCallBack];??
self.delegate = nil;?
? [[YTKNetworkAgent sharedInstance] cancelRequest:self];??
[self toggleAccessoriesDidStopCallBack];}
還有一個(gè)比較重要的增加hook的方法,需要自定義個(gè)對(duì)象涧窒,實(shí)現(xiàn)YTKRequestAccessory協(xié)議定義的一些方法來hook一些動(dòng)作- (void)addAccessory:(id)accessory {
// 因?yàn)榭赡苡卸鄠€(gè)hook對(duì)象心肪,所以添加到一個(gè)數(shù)組中,調(diào)用的時(shí)候也是遍歷數(shù)組調(diào)用
if (!self.requestAccessories) {
self.requestAccessories = [NSMutableArray array];
}
[self.requestAccessories addObject:accessory];
}
YTKRequest
這里主要實(shí)現(xiàn)了一些緩存策略纠吴,重寫了父類的start方法
- (void)start {
if (self.ignoreCache) {
[super start];
return;
}
// 查看緩存時(shí)間是否過期
if ([self cacheTimeInSeconds] < 0) {
[super start];
return;
}
// 查看本地的緩存版本號(hào)和當(dāng)前緩存判斷是否匹配
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;
}
// 查看緩存時(shí)間是否過期
int seconds = [self cacheFileDuration:path];
if (seconds < 0 || seconds > [self cacheTimeInSeconds]) {
[super start];
return;
}
// 加載緩存數(shù)據(jù)
_cacheJson = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if (_cacheJson == nil) {
[super start];
return;
}
_dataFromCache = YES;
// 調(diào)用緩存結(jié)束回調(diào)
[self requestCompleteFilter];
YTKRequest *strongSelf = self;
[strongSelf.delegate requestFinished:strongSelf];
if (strongSelf.successCompletionBlock) {
strongSelf.successCompletionBlock(strongSelf);
}
[strongSelf clearCompletionBlock];
}
緩存是存放在本地文件中的硬鞍,文件名用一些關(guān)鍵字的字符串拼接并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
這個(gè)類主要負(fù)責(zé)一些配置的工作,配置baseUrl戴已,cdnUrl等工作固该,內(nèi)部沒有什么具體的實(shí)現(xiàn),在其他類中獲取這個(gè)類的配置信息
YTKBatchRequestAgent糖儡、YTKBatchRequest
用于方便地發(fā)送批量的網(wǎng)絡(luò)請(qǐng)求伐坏,YTKBatchRequest是一個(gè)容器類,它可以放置多個(gè) YTKRequest 子類握联,并統(tǒng)一處理這多個(gè)網(wǎng)絡(luò)請(qǐng)求的成功和失敗桦沉。
在如下的示例中,我們發(fā)送了4個(gè)批量的請(qǐng)求金闽,并統(tǒng)一處理這4個(gè)請(qǐng)求同時(shí)成功的回調(diào)纯露。
- (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");
}];
}
內(nèi)部實(shí)現(xiàn),start方法遍歷所有request代芜,并調(diào)用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];
}
}
在成功回調(diào)中埠褪,有一個(gè)計(jì)數(shù)器,判斷所有請(qǐng)求是否都已經(jīng)完成
- (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
用于管理有相互依賴的網(wǎng)絡(luò)請(qǐng)求钞速,它實(shí)際上最終可以用來管理多個(gè)拓?fù)渑判蚝蟮木W(wǎng)絡(luò)請(qǐng)求。
以下是具體的代碼示例嫡秕,在示例中渴语,我們?cè)趕endChainRequest方法中設(shè)置好了Api相互的依賴,然后昆咽。 我們就可以通過chainRequestFinished回調(diào)來處理所有網(wǎng)絡(luò)請(qǐng)求都發(fā)送成功的邏輯了遵班。如果有任何其中一個(gè)網(wǎng)絡(luò)請(qǐng)求失敗了,則會(huì)觸發(fā)chainRequestFailed回調(diào)潮改。
- (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
}
內(nèi)部實(shí)現(xiàn)狭郑,定義一個(gè)_nextRequestIndex,初始化為0汇在,_requestArray請(qǐng)求數(shù)組翰萨,_requestCallbackArray請(qǐng)求回調(diào)數(shù)組
- (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.");
}}
// 順序執(zhí)行請(qǐng)求,_nextRequestIndex++
- (BOOL)startNextRequest {
if (_nextRequestIndex < [_requestArray count]) {
YTKBaseRequest *request = _requestArray[_nextRequestIndex];
_nextRequestIndex++;
request.delegate = self;
[request start];
return YES;
} else {
return NO;}}
// 請(qǐng)求成功回調(diào)
- (void)requestFinished:(YTKBaseRequest *)request {
// 獲取當(dāng)前請(qǐng)求的回調(diào)糕殉,并調(diào)用其回調(diào)亩鬼,回調(diào)中需要用戶自己去再次去add一個(gè)新的request
NSUInteger currentRequestIndex = _nextRequestIndex - 1;
ChainCallback callback = _requestCallbackArray[currentRequestIndex];
callback(self, request);
// 當(dāng)不能繼續(xù)執(zhí)行請(qǐng)求時(shí)殖告,結(jié)束本次chain請(qǐng)求,調(diào)用完成回調(diào)
if (![self startNextRequest]) {
[self toggleAccessoriesWillStopCallBack];
if ([_delegate respondsToSelector:@selector(chainRequestFinished:)]) {
[_delegate chainRequestFinished:self];
[[YTKChainRequestAgent sharedInstance] removeChainRequest:self];
}
[self toggleAccessoriesDidStopCallBack];}}
YTKNetworkPrivate
定義一些內(nèi)部使用的工具方法