美文网首页
NSURLSession

NSURLSession

作者: 952625a28d0d | 来源:发表于2017-02-21 00:39 被阅读26次

    URLConnection

    URL -> NSURLRequest -> URLConnect

    NSURLSession

    DataTask
    DownloadTask
    UploadTask
    以及断点续传、后台下载
    所有的任务都是我们的Session发起的
    默认所有的任务都是挂起的,需要我们resume
    第一次resume就是Start
    所有的任务都可以挂起

    • 举个例子
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        // 1. url
        NSURL *url = [NSURL URLWithString:@"http://www.weather.com.cn/data/sk/101190408.html"];
        [self taskWithUrl:url];
    }
    
    #pragma mark -- 开始一个请求
    - (void)taskWithUrl:(NSURL *)url{
        // 2.苹果提供了一个全局的单例
        NSURLSession *session = [NSURLSession sharedSession];
        //    NSURLRequest *request = [NSURLRequest requestWithURL:url];    // 可以省略这一步 让task内部自动创建
        // 3.发送我们的网络任务
        NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            NSLog(@"%@",result);
        }];
        // 4.启动任务
        [dataTask resume];
    }
    
    Paste_Image.png
    • 使用URLSession下载
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        NSURL *url = [NSURL URLWithString:@"http://v.ku6.com/playlistVideo.htm?t=list&p=1&playlistId=2196066"];
        // 使用下载方法下载
        /*
         如果在回调方法中,不做任何处理,下载的文件会被删除
         下载文件放在temp里面,系统会自动回收这块区域!
         设计的目的是什么?
         - 通常从网络上下载文件,什么格式最多?-》zip文件最多!可以替代用户节约流量!!
         - 如果是zip包,下载之后,需要解压
         - 解压缩之后,原始的zip文件就不再需要了,系统会自动帮助我们删掉!
         */
        [[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSLog(@"%@",location);
        }];
    }
    
    • 文件解压缩
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        NSURL *url = [NSURL URLWithString:@""];
        
        [[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            // 下载结束 解压缩到目标路径
            // 目标路径
            NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
            // 只需要知道目标路径,不需要知道目标文件
            // 因为一个压缩包中有可能有多个文件
            [SSZipArchive unzipFileAtPath:location.path toDestination:cachePath];
        }] resume];
    }
    

    下面 来搞一个断点续传

    • 先搞一个ProgressView
    #import <UIKit/UIKit.h>
    
    @interface JYFProgressButton : UIButton
    
    /**
     进度
     */
    @property (nonatomic, assign) float progress;
    
    @end
    
    #import "JYFProgressButton.h"
    
    @implementation JYFProgressButton
    
    - (void)setProgress:(float)progress{
        _progress = progress;
        
        // 设置Title
        [self setTitle:[NSString stringWithFormat:@"%.02f%%",_progress * 100] forState:(UIControlStateNormal)];
        
        // 刷图视图 会调用drawRect
        [self setNeedsDisplay];
    }
    
    /*
     写一个宏 myMIN(a,b) 返回最小值
     写一个宏 myMIN3(a,b,c) 返回最小值
     */
    
    #define myMIN(a,b) (((a) < (b))?(a):(b))
    #define myMIN3(a,b,c) myMIN(myMIN(a,b,),c)
    
    #pragma mark -- 画个圆
    - (void)drawRect:(CGRect)rect{
        
        // 圆心
        CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2);
        // 半径
    //    CGFloat r = (rect.size.height > rect.size.width)? rect.size.width * 0.4 : rect.size.height * 0.4;
        CGFloat r = myMIN(rect.size.width, rect.size.height) * 0.4;
        // 从什么地方开始
        CGFloat startAng = - M_PI_2; // 从180度开始
        // 到什么地方结束
        CGFloat endAng = self.progress * 2 * M_PI + startAng; // 两倍的PI就是一个圆
        /*
         1.圆心
         2.半径
         3.起始角度
         4.结束角度
         5.顺时针
         */
        // 使用贝塞尔曲线来进行绘制
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:r startAngle:startAng endAngle:endAng clockwise:YES];
        
        // 设置线条宽度
        path.lineWidth = 10;
        // 设置线条风格
        path.lineCapStyle = kCGLineCapRound;
        // 填充颜色
        [[UIColor blueColor] setStroke];
        // 绘制路径
        [path stroke];
    }
    
    • 实现下载以及断点续传功能
    #import "ViewController.h"
    #import "JYFProgressButton.h"
    
    @interface ViewController ()<NSURLSessionDownloadDelegate>
    
    // 管理全局的session任务
    @property (nonatomic, strong) NSURLSession *session;
    
    @property (weak, nonatomic) IBOutlet JYFProgressButton *progressView;
    
    /**
     全局的下载任务
     */
    @property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
    
    /**
     暂停的时候已经下载的数据
     */
    @property (nonatomic, strong) NSData *resumeData;
    
    @end
    
    @implementation ViewController
    
    - (NSURLSession *)session{
        if (!_session) {
            // config 提供了一个全局的网络环境配置 包括身份验证 浏览器类型 cookie 缓存 超时
            NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
            // 设置代理的队列
            /*
             队列:设置回调的代理方法在哪里执行 这里只是应用于回调 那么下载一定是在异步执行的
                 - 代理的队列 如果给 nil 在多个线程中执行时没有问题的
                 - [NSOperationQueue mainQueue] 主队列 可以给nil 默认子线程
             Session 会对代理强引用 如果任务结束后 不取消Session 会出现内存泄露
             // 真正的网络访问
             - 在网络开发中,应该将所有的网络访问操作,封装到一个方法中,由一个统一的单例对象负责所有的网络事件
             - Session对代理(单例)进行强引用!单例本身就是一个静态的实例,本身不需要释放
             - AFN -> 需要建立一个AFN 的Manager
             */
            
            _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        }
        return _session;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    #pragma mark -- NSURLSession Delegate
    #pragma mark -- 下载完成 7.0以下三个方法  都要实现,但是8.0就不需要了 只需要实现下载完成就好了
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
        // 任务完成
        [self.session finishTasksAndInvalidate]; // 只是内部干掉了代理 并没有清空
        // 清空我们的Session 为了让它下载懒加载的时候重新初始化
        self.session = nil;
        NSLog(@"%@",location);
    }
    
    #pragma mark -- 下载续传
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
        
    }
    
    #pragma mark -- 下载进度监听的方法
    /*
     1.session
     2.downloadTask 调用代理方式的下载任务
     3.bytesWritten 本次下载的字节数
     4.totalBytesWritten 已经下载的字节数
     5.totalBytesExpectedToWrite 期望下载的总大小
     */
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
        
        // 下载进度
        float progress = (float) totalBytesWritten / totalBytesExpectedToWrite;
        NSLog(@"%f",progress);
        
        // 打印当前线程
        NSLog(@"%f %@",progress,[NSThread currentThread]);
        
        // 回到主线程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.progressView.progress = progress;
        });
    }
    
    #pragma mark -- 开始下载
    - (IBAction)start:(UIButton *)sender {
        NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.1.dmg"];
        // 使用下载方法下载
        // 如果需要下载进度,也需要通过监听 NSURLSession和NSURLConnection一样都是通过代理!!
        // NSURLSession 它的全局的 Session 单例是整个系统的
        // NSURLSession是全局的单例 是整个系统的 那么我们设置代理不能全局设置
        /*
         如果需要跟进下载进度,不能使用快代码回调的方式
         如果使用Block 默认是子线程
         所以 要回到主线程
         */
        //    [[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        //        NSLog(@"%@",location);
        //    }];
        // 需要使用代理的方式
    //    [[self.session downloadTaskWithURL:url] resume];
        
        // 断点续传 需要使用全局的downloadTask 下载
        self.downloadTask = [self.session downloadTaskWithURL:url];
        // 开始任务
        self.resumeData = nil;
        [self.downloadTask cancel];
        [self.downloadTask resume];
    }
    
    #pragma mark -- 暂停下载
    - (IBAction)pause:(UIButton *)sender {
        
        // 暂停 肯定是暂停任务
        [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
            // resumeData : 续传的数据(下载了多少)
            NSLog(@"数据的长度:%tu",resumeData.length);
            
            self.resumeData = resumeData;
            // 释放下载任务 我们task设置为weak 就可以不用释放了 因为我们的任务都是由Session发起的 而Session对发起的任务都会持有一个Strong 都会持有一个强引用 如果不手动释放 非常危险
            // 解决Session强引用的问题 就必须让Session释放掉
            self.downloadTask = nil;
        }];
    }
    
    #pragma mark -- 继续下载
    - (IBAction)resume:(UIButton *)sender {
        
        NSLog(@"%ld",(long)self.downloadTask.state);
        if (self.resumeData == nil) {
            return;
        }
        // 继续下载 继续下载任务 也是应该由Session发起的
        // 使用续传数据启动下载任务
        self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
        // 清空续传数据
        self.resumeData = nil;
        // 所有的任务都是默认挂起的
        [self.downloadTask resume];
    }
    
    - (void)viewDidDisappear:(BOOL)animated{
        [super viewDidDisappear:animated];
        
        // 取消会话
        [self.session invalidateAndCancel];
        self.session = nil;
    }
    
    @end
    
    URLSession.gif

    相关文章

      网友评论

          本文标题:NSURLSession

          本文链接:https://www.haomeiwen.com/subject/zwgewttx.html