序言
AFNetworking2.0和3.0区别很大,也是因为苹果废弃了NSURLConnection,而改用了NSURLSession,AFNetworking3.0实际上只是对NSURLSession所做的操作进行了高度封装,提供更加简洁的API供编码调用。
查看AFHTTPSessionManager.h文件,可知AFHTTPSessionManager是AFURLSessionManager的子类:
@interface AFHTTPSessionManager : AFURLSessionManager
- 实例代码 - 下载一张图片
// 下载图片并且保存
- (void)downloadImgAndSave {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"image/jpeg", nil];
__weak __typeof__(self) weakSelf = self;
NSString *url = @"http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg";
// 开始下载
[manager GET:url parameters:nil progress:^(NSProgress *downloadProgress) {
NSLog(@"progress:%lld",downloadProgress.completedUnitCount);
} success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"图片下载完成");
__strong __typeof__(weakSelf) strongSelf = weakSelf;
strongSelf.imgView.image = [UIImage imageWithData:(NSData *)responseObject];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if (error) {
NSLog(@"%@",error.userInfo);
}
}];
}
运行结果
一 GET请求调用函数分析
- AFHTTPSessionManager.m
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
- AFURLSessionManager.m
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
二 AFN的GET和POST请求实现第二层 - AFURLSessionManager
- 实例代码 - 下载一个较大文件
// 下载一个大文件
- (void)downloadBigFile {
// 1.创建配置参数
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// 2.创建会话管理者
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
// 3.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"]];
// 4.创建下载任务
/**
* 第一个参数 - request:请求对象
* 第二个参数 - progress:下载进度block
* 其中: downloadProgress.completedUnitCount:已经完成的大小
* downloadProgress.totalUnitCount:文件的总大小
* 第三个参数 - destination:自动完成文件剪切操作
* 其中: 返回值:该文件应该被剪切到哪里
* targetPath:临时路径 tmp NSURL
* response:响应头
* 第四个参数 - completionHandler:下载完成回调
* 其中: filePath:真实路径 == 第三个参数的返回值
* error:错误信息
*/
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress) {
__weak typeof(self) weakSelf = self;
// 获取主线程,不然无法正确显示进度。
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
weakSelf.progressLbe.text = [NSString stringWithFormat:@"当前下载进度:%.2f%%",100.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount];
}];
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSURL *path = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [path URLByAppendingPathComponent:response.suggestedFilename];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSLog(@"File downloaded to: %@", filePath);
}];
// 5.开启下载任务
[downloadTask resume];
}
运行结果
内部封装分析 AFURLSessionManager.m
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
其中self.session
是NSURLSession
的一个实例对象,dataTaskWithRequest:
是其创建任务的一个方式。
@property (readonly, nonatomic, strong) NSURLSession *session;
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
添加代理的封装
- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
if (destination) {
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:downloadTask];
delegate.downloadProgressBlock = downloadProgressBlock;
}
其中setDelegate:forTask
的实现
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
其中mutableTaskDelegatesKeyedByTaskIdentifier
是一个可变字典
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
被调用的地方在
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
进而调用的是系统的代理
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (self.downloadTaskDidFinishDownloading) {
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
在系统
NSURLSessionDelegate
回调方法中,下面方法被调用多次,即将任务
作为key
,从字典中取出对应的delegate
,再回调出去,最终回调到应用层面。
我们看看AFURLSessionManager
的方法目录结构就知道了
- 下图是
NSURLSessionDelegate
方法目录结构
- 下图是
AFURLSessionDelegate
方法目录结构,和系统的NSURLSessionDelegate
很像,别弄混了。
三 dataTaskWithRequest:completionHandler:函数详解
说明:这个NSURLSession的API容易跟AFURLSessionManager的API混淆,参数都是一个request和一个handler block。
- NSURLSession的API是这样的
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
- 而AFURLSessionManager的API是这样的
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError * error))completionHandler DEPRECATED_ATTRIBUTE;
- 实例代码
// 下载任务执行块
- (void)dataTaskWithRequest {
// 下载地址
NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"];
// 创建request请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置HTTP请求头中的Range
NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
// 开始下载任务
__weak typeof(self) weakSelf = self;
NSURLSessionDataTask *downloadTask = [self.manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
// 下载完成
// 清空长度
weakSelf.currentLength = 0;
weakSelf.fileLength = 0;
// 关闭fileHandle
[weakSelf.fileHandle closeFile];
weakSelf.fileHandle = nil;
}];
}
其中manager
是懒加载得到的
- (AFURLSessionManager *)manager {
if (_manager == nil) {
_manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
}
return _manager;
}
- AF内部封装实现
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
}
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
四 总结
AF请求操作内部实现也是和原生NSURLSession操作一样,创建task,调用resume发送请求。
项目连接地址 - AFNetworkingDemo