原创blog,转载请注明出处
blog.csdn.net/hello_hwc
欢迎关注我的IOS-SDK讲解专栏
http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html
前言:
这是IOS 网络开发系列的第三篇文章,这篇文章主要介绍了NSURLSession以及NSURLSessionTask这个抽象类,和NSURLSessionDataTask的使用和代理方法。
本篇的顺序,
1. demo效果
2. NSURLSessionTask属性方法介绍
3. NSURLSessionDataTask的使用和代理方法
4. Demo的源码讲解
Task是由Session创建的,Session会保持对Task的一个强引用,直到Task完成或者出错才会释放。通过NSURLSessionTask可以获得Task的各种状态,以及对Task进行取消,挂起,继续等操作。一共包括三种Task,三种Task的结构如图。本文主要讲解的是DataTask。
管理task的状态的方法
- (void)cancel //取消一个task
- (void)resume //如果task在挂起状态,则继续执行task
- (void)suspend //挂起task
获得task的执行情况的属性
countOfBytesExpectedToReceive
countOfBytesReceived
countOfBytesExpectedToSend
countOfBytesSent
task的综合信息
currentRequest // 当前活跃的request
originalRequest // 在task创建的时候传入的request(有可能会重定向)
response // 服务器对当前活跃请求的响应
taskDescription // 描述当前task
taskIdentifier // 用来区分Task的描述符
error //Task失败的错误信息
task状态的枚举
typedef NS_ENUM (NSInteger,
NSURLSessionTaskState ) {
NSURLSessionTaskStateRunning = 0,
NSURLSessionTaskStateSuspended = 1,
NSURLSessionTaskStateCanceling = 2,
NSURLSessionTaskStateCompleted = 3,
};
DataTask是用来干嘛的呢?
用来下载数据到内存里,数据的格式是NSData
dataTask使用的过程中,有两种方式来处理结果
举个例子
下载一幅图片,完成后显示到ImageView。下面代码看起来很简单吧。
NSURLSessionDataTask * task = [self.session dataTaskWithURL:[NSURL URLWithString:imageURL] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
UIImage * image = [UIImage imageWithData:data];
self.imageview.image = image;
}
}];
[task resume];
主要使用到三种代理中的事件
NSURLSessionDelegate用来处理Session层次的事件
Session被 invalide得到的事件
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error
Session层次收到了授权,证书等问题
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *credential))completionHandler
NSURLSessionTaskDelegate是使用代理的时候,任何种类task都要实现的代理
Task完成的事件
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
Task层次收到了授权,证书等问题
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *credential))completionHandler
将会进行HTTP,重定向
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
NSURLSessionDataDelegate特别用来处理dataTask的事件
收到了Response,这个Response包括了HTTP的header(数据长度,类型等信息),这里可以决定DataTask以何种方式继续(继续,取消,转变为Download)
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
DataTask已经转变成DownloadTask
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
每收到一次Data时候调用
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
是否把Response存储到Cache中
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDataDelegate>
@property (strong,nonatomic)UIImageView * imageview;
@property (strong,nonatomic)NSURLSession * session;
@property (strong,nonatomic)NSURLSessionDataTask * dataTask;
@property (weak, nonatomic) IBOutlet UIProgressView *progressview;
@property (nonatomic)NSUInteger expectlength;
@property (strong,nonatomic) NSMutableData * buffer;
@end
static NSString * imageURL = @"http://f12.topit.me/o129/10129120625790e866.jpg";
@implementation ViewController
//属性全部采用惰性初始化
#pragma mark - lazy property
-(UIImageView *)imageview{
if (!_imageview) {
_imageview = [[UIImageView alloc] initWithFrame:CGRectMake(40,40,300,200)];
_imageview.backgroundColor = [UIColor lightGrayColor];
_imageview.contentMode = UIViewContentModeScaleToFill;
}
return _imageview;
}
-(NSMutableData *)buffer{
if (!_buffer) {
_buffer = [[NSMutableData alloc] init];
}
return _buffer;
}
-(NSURLSession*)session{
if (!_session) {
NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
-(NSURLSessionDataTask *)dataTask{
if (!_dataTask) {
_dataTask = [self.session dataTaskWithURL:[NSURL URLWithString:imageURL]];
}
return _dataTask;
}
#pragma mark - life circle of viewcontroller
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.imageview];
[self.dataTask resume];//Task要resume彩绘进行实际的数据传输
[self.session finishTasksAndInvalidate];//完成task就invalidate
}
#pragma mark - target-action
//注意判断当前Task的状态
- (IBAction)pause:(UIButton *)sender {
if (self.dataTask.state == NSURLSessionTaskStateRunning) {
[self.dataTask suspend];
}
}
- (IBAction)cancel:(id)sender {
switch (self.dataTask.state) {
case NSURLSessionTaskStateRunning:
case NSURLSessionTaskStateSuspended:
[self.dataTask cancel];
break;
default:
break;
}
}
- (IBAction)resume:(id)sender {
if (self.dataTask.state == NSURLSessionTaskStateSuspended) {
[self.dataTask resume];
}
}
#pragma mark - URLSession delegate method
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSUInteger length = [response expectedContentLength];
if (length != -1) {
self.expectlength = [response expectedContentLength];//存储一共要传输的数据长度
completionHandler(NSURLSessionResponseAllow);//继续数据传输
}else{
completionHandler(NSURLSessionResponseCancel);//如果Response里不包括数据长度的信息,就取消数据传输
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"error"
message:@"Do not contain property of expectedlength"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
[self.buffer appendData:data];//数据放到缓冲区里
self.progressview.progress = [self.buffer length]/((float) self.expectlength);//更改progressview的progress
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (!error) {
dispatch_async(dispatch_get_main_queue(), ^{//用GCD的方式,保证在主线程上更新UI
UIImage * image = [UIImage imageWithData:self.buffer];
self.imageview.image = image;
self.progressview.hidden = YES;
self.session = nil;
self.dataTask = nil;
});
}else{
NSDictionary * userinfo = [error userInfo];
NSString * failurl = [userinfo objectForKey:NSURLErrorFailingURLStringErrorKey];
NSString * localDescription = [userinfo objectForKey:NSLocalizedDescriptionKey];
if ([failurl isEqualToString:imageURL] && [localDescription isEqualToString:@"cancelled"]) {//如果是task被取消了,就弹出提示框
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Message"
message:@"The task is canceled"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}else{
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Unknown type error"//其他错误,则弹出错误描述
message:error.localizedDescription
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
self.progressview.hidden = YES;
self.session = nil;
self.dataTask = nil;
}
}
@end