前言
公司最近刚刚赶完一个版本需求,准备对AFNetworking进行升级。其实年前就做过这方面研究,当时是因为有传言2017年后苹果要求升级https而老的网络底层不支持,必须升级。后来因为新需求比较赶,又在网上找到可靠消息说网上都是谣传并没有强制升级于是就搁置了下来。
简介
AFNetworking是对IOS本身网络请求的封装,有两种方式NSURLConnection和NSURLSession。
因为AFNetworking1.0就是构建于NSURLConnection之上的,等到AFNetworking2.0才NSURLSession的方式,之后的AFNEtworking3.0更去掉了NSURLConnection的方式。关于原因可以点击这里。
所以这一次我的主要任务就是从之前的NSURLConnection(AFHTTPRequestOperationManager)升级到NSURLSession(AFHTTPSessionManager)。
当前网络层
刚开始我得先把当前的网络层理一下,看下哪些地方用到网络请求。最后发现也复杂,一共两种不一样的地方:
1.最主要的一个就是对一般网络POST请求的封装。
2.上传一些诸如图片、音频等文件的PUT请求。
看上去挺简单的,但因为代码经过多代人之手,难免有些冗余代码质量也不是特别高,就像上传附件的封装就有两份在程序中都有用到,为了尽量不产生新问题我也就对它们都做修改。
下面是之前的实现方式的主要流程,一些代码上的坑我就不放出来了。
1.首先是一般网络POST请求的封装。
(1)首先是单例的实现基础设置:
+ (instancetype) sharedClient{
static NetworkManager *_sharedClient = nil;
static dispatch_once_t _onceTocken;
dispatch_once(&_onceTocken, ^{
if (appDelegate.apiBaseUrlStr) {
_sharedClient = [[NetworkManager alloc] initWithBaseURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@",appDelegate.apiBaseUrlStr]]];
} else {
_sharedClient = [[NetworkManager alloc] initWithBaseURL:[NSURL URLWithString:@"(null)"]];
}
_sharedClient.llOperationQueue = [[NSOperationQueue alloc] init];
//最大并发数
_sharedClient.llOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount;
[_sharedClient.requestSerializer setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[_sharedClient.requestSerializer setValue:ApiVersion forHTTPHeaderField:@"User-Agent"];
[_sharedClient.requestSerializer setTimeoutInterval:30];
_sharedClient.bstart = NO;
_sharedClient.apiCount = 0;
});
return _sharedClient;
}
(2)POST请求对外接口:
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nonnull))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
if (![CommonClass checkNetworkRechable]) {
failure(nil,[NSError errorWithDomain:@"www.mHealth.com" code:-1009 userInfo:nil]);
return nil;
}
if((!appDelegate.apiBaseUrlStr || appDelegate.apiBaseUrlStr.length < 1) && !_bstart)
{
#ifdef DEBUG_MODE
NSLog(@"No baseurl, need network access");
#endif
[self.llOperationQueue setSuspended:true];
LandbalancerSessionOperation *llsessionOperation = [[LandbalancerSessionOperation alloc] initWithURLString:URLString parameters:parameters success:success failure:failure];
[self.llOperationQueue addOperation:llsessionOperation];
_bstart = true;
[self requestApi];
return nil;
}
else
{
return [super POST:URLString parameters:parameters progress:uploadProgress success:success failure:failure];
}
}
这边有个判断,如果没有baseurl,则要把当次请求先挂起,先去请求version接口,完成后再开始当次请求。
2.附件上传
我们这边上传附件的流程是这样的,先进行POST请求将我们拼接好的文件名和文件大小给云端,云端这时候会返回我们需要上传到的url,我们再通过这个url进行PUT上传。
(1)POST请求将文件名和长度给云端:
#pragma mark - NSOperation
-(void)main
{
[self.lock lock];
if(!self.file.rawData)
{
return;
}
NSUInteger size = self.file.rawData.length;
if (size <= 0)
{
// [self removeOperationFromDB];
return;
}
if(!self.file.key || self.file.key.length < 1)
{
return;
}
NSMutableDictionary *parameterMDic = [[NSMutableDictionary alloc] init];
[parameterMDic setObject:[NSNumber numberWithInteger:size] forKey:@"size"];
[parameterMDic setObject:self.file.key forKey:@"filename"];
[parameterMDic setObject:[appDelegate.userDefaults objectForKey:AccessToken] forKey:AccessToken];
[[NetworkManager sharedClient] POST:API_ATTACHMENT parameters:parameterMDic success:^
(AFHTTPRequestOperation *__unused task, id JSON){
#ifdef DEBUG_MODE
#endif
[self completionBlock:API_ATTACHMENT withData:JSON withUserInfo:nil];
}failure:^
(AFHTTPRequestOperation *__unused task, NSError *error){
#ifdef DEBUG_MODE
#endif
[self failedBlock:API_ATTACHMENT withError:error withUserInfo:nil];
}];
[self.lock unlock];
}
(2)这里把云端返回数据统一判断,未完成就进行PUT请求上传文件,直到完成:
-(void)dealAttachmentResponse:(NSDictionary *)responseDic
{
[self.lock lock];
NSString *finishStr = [responseDic objectForKey:@"finished"];
if ([CommonClass bNull:(NSNull *)finishStr] && finishStr)
{
_finishStr = finishStr;
}
else
{
_finishStr = nil;
}
NSNumber *lenNum = [responseDic objectForKey:@"len"];
NSString *methodStr = [responseDic objectForKey:@"method"];
if ([CommonClass bNull:(NSNull *)methodStr] && methodStr)
{
_methodStr = methodStr;
}
else
{
_methodStr = nil;
}
NSNumber *offNum = [responseDic objectForKey:@"off"];
NSString *urlStr = [responseDic objectForKey:@"url"];
if ([CommonClass bNull:(NSNull *)urlStr] && urlStr)
{
_urlStr = urlStr;
}
else
{
_urlStr = nil;
}
// _urlStr = [_urlStr stringByReplacingOccurrencesOfString:@"db.mcloudlife.com" withString:@"115.236.76.184"];
if ([_finishStr isEqualToString:@"Y"] || [lenNum integerValue] <= 0)
{
//上传完成
// NSString *imgName = [urlStr lastPathComponent];
// self.uploadOneFileSucceeded(imgName, self.fileIndex, self.file.key);
self.uploadOneFileSucceeded(_urlStr, self.fileIndex, self.file.key);
return;
}
NSRange range = NSMakeRange([offNum integerValue], [lenNum integerValue]);
NSData * data = [self getUploadImgData:self.file.rawData withRange:range];
NSInputStream *inputStream = [[NSInputStream alloc] initWithData:data];
[self httpRequestThroughPut:inputStream];
[self.lock unlock];
}
- (NSData *)getUploadImgData:(NSData *)imgData withRange:(NSRange)range
{
NSUInteger length = [imgData length];
if (length < (range.location + range.length)) {
return nil;
}
NSData *subData = [imgData subdataWithRange:range];
return subData;
}
(3)PUT请求上传数据,失败重试,成功则再以上面的方法判断是否完成。
#pragma mark - NetWork_Upload
-(void)httpRequestThroughPut:(NSInputStream *)inputStream
{
if (inputStream && _urlStr)
{
AFHTTPRequestOperationManager *requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:nil];
//设置httpHeader
[requestOperationManager.requestSerializer setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[requestOperationManager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[requestOperationManager.requestSerializer setValue:[NSString stringWithFormat:@"mCloud %@",[appDelegate.userDefaults objectForKey:AccessToken]] forHTTPHeaderField:@"Authorization"];
[requestOperationManager.requestSerializer setValue:@"db.mcloudlife.com" forHTTPHeaderField:@"Host"];
//设置超时时间
[requestOperationManager.requestSerializer setTimeoutInterval:-1];
//put请求
AFHTTPRequestOperation *requestOperation = [requestOperationManager PUT:_urlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (![NetworkErrorDeal getErrorMsgWithResponseData:responseObject])
{
if(![responseObject isKindOfClass:[NSDictionary class]])
{
return;
}
[self dealResponse:responseObject];
}
else
{
//尝试重新请求
if(_retryCount <= 0)
{
return ;
}
_retryCount--;
dispatch_queue_t queue= dispatch_queue_create("dealFileAttachmentResponse", NULL);
dispatch_async(queue, ^{
[self httpRequestThroughPut:inputStream];
});
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if(_retryCount <= 0)
{
return ;
}
_retryCount--;
dispatch_queue_t queue= dispatch_queue_create("dealImageAttachmentResponse", NULL);
dispatch_async(queue, ^{
[self httpRequestThroughPut:inputStream];
});
}];
__block AFHTTPRequestOperation *progressOperation = requestOperation;
[requestOperation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// }]
// [requestOperation setUploadProgressBlock:^(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {
double percent = totalBytesWritten * 1.0 / self.file.fileSize;
if (self.uploadOneFileProgress) {
self.uploadOneFileProgress(progressOperation, self.fileIndex, percent);
}
}];
//设置httpbodystream
[requestOperation setInputStream:inputStream];
}
}
好了,这些就是之前的网络层,等我给测试进行整体测试如果没问题的话,我会在下一篇记录主要的修改。
另外大家如果有关于升级AFNetworking3.0的问题的话可以留下评论,指教不敢当,互相讨论吧:)