• NSURLSession学习


    一、回望NSURLConnection

    作为Core Foundation / CFNetwork 框架的APIs之上的一个抽象,NSURLConnection伴随着2003年Safari浏览器的原始发行版本而诞生。

    NSURLConnection实际上指的是一组构成Foundation框架中URL加载系统的相互关联的组件:NSURLRequest,NSURLResponse,NSURLProtocol,NSURLCache,NSHTTPCookieStorage,NSURLCredentialStorage,以及和它同名NSURLConnection。

    二、从NSURLConnection到NSURLSession

    在WWDC 2013中,Apple的团队对NSURLConnection进行了重构,并推出了NSURLSession在iOS9中,苹果已经废除了NSURLConnection的使用,使用NSURLSession代替。AFNetworking最新版也已经从依赖于NSURLConnection改为NSURLSession。

    参见:https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html

    NSURLSession也是一组相互依赖的类,它的大部分组件和NSURLConnection中的组件相同如NSURLRequest,NSURLCache等。而NSURLSession的不同之处在于,它将NSURLConnection替换为NSURLSessionNSURLSessionConfiguration,以及3个NSURLSessionTask的子类:NSURLSessionDataTask, NSURLSessionUploadTask, 和NSURLSessionDownloadTask。

    在程序在前台时,NSURLSession与NSURLConnection可以互为替代工作;程序在后台或者程序未运行时,NSURLSession可以在后台继续工作。注意,如果用户强制将程序关闭,NSURLSession会断掉。

    三、使用方法:

    1.创建一个NSURLSessionConfiguration,为创建NSURLSession设置工作模式和网络;

    sessionConfiguration一旦创建,再修改对已经创建的session就不起作用。除非在URLRequest中改写了更严格的策略。 

    2.工作模式:

    一般模式(default):工作模式类似于原来的NSURLConnection,可以使用缓存到硬盘的Cache,Cookie,鉴权。

    及时模式(ephemeral):不使用缓存的Cache,Cookie,鉴权(session相关数据保存在内存中),适用于私密浏览和其它类似情况。

    后台模式(background):在后台完成上传下载,创建Configuration对象的时候需要给一个NSString的ID用于追踪完成工作的Session是哪一个。

    3.创建一个NSURLSession,方法有三种:

    1 + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;

    这种方法根据刚才创建的sessionConfiguration创建session,并默认创建一个新的queue处理session信息。

     1 + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration 2 delegate:(id<NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue; 

    这种方法可以设置回调的delegate和queue,如果设置在mainQueue,就能在主线程进行回调。如果不指定线程,默认就是子线程。 
    注意:session对delegateQueue是强应用,如果不停止session或者退出应用,它会一直存在在内存中。

    1  + (NSURLSession *)sharedSession;

    这种方法创建的session,使用使用共享的会话,全局的Cache,Cookie和证书。但是:不能逐渐获取服务器数据、自定义连接行为受限、鉴权受限、app未运行情况下无法后台上传和下载。

    4.创建一个NSURLRequest调用刚才的NSURLSession对象提供的Task函数,创建一个NSURLSessionTask。 
    根据职能不同Task有三种子类: 
    1)NSURLSessionUploadTask:上传用的Task,传完以后不会再下载返回结果; 
    通过request创建,在上传时指定文件源或数据源。

    1 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
    2 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;
    3 - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;

    在创建upload task对象时,通过completionHandler指定任务完成后的回调代码块:

    1 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
    2 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

    2)NSURLSessionDownloadTask:下载用的Task; 
    通过request对象或url创建:

    1 - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
    2 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;

    通过request对象或url创建,同时指定任务完成后通过completionHandler指定回调的代码块:

    1 - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
    2 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;

    根据已经下载的数据,继续下载任务,也有直接创建和任务完成后通过completionHandler指定回调的代码块两种:

    1 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
    2 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHa

    3)NSURLSessionDataTask:可以上传内容,上传完成后再进行下载。 

    通过request对象或url创建:

    1 - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
    2 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request; 

    通过request对象或url创建,同时指定任务完成后通过completionHandler指定回调的代码块:

    1 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;  
    2 - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;  

     4.NSURLSessionDelegate

    1)session失败时会调用,如果使用- (void)invalidateAndCancel;方法会立即调用,并给出错误原因

    1 - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error;

    2) 当遇到验证请求时会调用,completionHandler中应当包含如何处置验证请求和用于验证的证书

    1 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

    3)当后台session完成后会调用

    1 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session;

    5.NSURLSessionDelegate

    1)当task以错误结束的时候调用

    1 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;

    2)task请求需要验证时调用

    1 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

    3)task向服务器发送数据时调用

    1 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;

    4)task需要向远程服务器发送一个新的body stream时调用

    1 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler; 

    5)task需要HTTP重定向时调用

    1 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler

    6.NSURLSessionDataDelegate

    1)dataTask接收到响应时调用

    1 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;

    2)dataTask变成downloadTask时调用

    1 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask; 

    1)dataTask接收到数据时调用 

    1 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data

     4)dataTask将缓存响应时调用

    1 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler;

    7.NSURLSessionDownloadDelegate

    1)downloadTask继续下载时调用,可以用来暂停和恢复下载

    1 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes;

    2)downloadTask写入数据时调用,可以用来计算下载进度

    1 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite; 

    3)downloadTask结束下载时调用

    1 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;

    8.使用详解

    1)如果是downloadTask创建的Session调用,Session与Delegate会在指定的OperationQueue中进行交互,交互过程的顺序图如下(假如不需要鉴权,即非HTTPS请求): 
    这里写图片描述 
    2)当不再需要连接时: 
    (1)调用Session的invalidateAndCancel直接关闭;

    (2)或者调用finishTasksAndInvalidate等待当前Task全部结束后关闭;

    这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用

    3)如果是一个BackgroundSession:在Task执行的时候,用户切到后台,Session会和ApplicationDelegate做交互。当程序切到后台后,在BackgroundSession中的Task还会继续下载,现在分三个场景分析下Session和Application的关系:

    (1)当加入了多个Task,程序没有切换到后台。 
    这种情况Task会按照NSURLSessionConfiguration的设置正常下载,不会和ApplicationDelegate有交互。

    (2)当加入了多个Task,程序切到后台,所有Task都完成下载。 
    在切到后台之后,Session的Delegate不会再收到Task相关的消息,直到所有Task全都完成后,系统会调用ApplicationDelegate的application:handleEventsForBackgroundURLSession:completionHandler:回调,之后“汇报”下载工作,对于每一个后台下载的Task调用Session的Delegate中的URLSession:downloadTask:didFinishDownloadingToURL:(成功的话)和URLSession:task:didCompleteWithError:(成功或者失败都会调用) 
    之后调用Session的Delegate回调URLSessionDidFinishEventsForBackgroundURLSession:。

    注意:在ApplicationDelegate被唤醒后,会有个参数ComplietionHandler,这个参数是个Block,这个参数要在后面Session的Delegate中didFinish的时候调用一下,如下:

    
    
     1 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier   completionHandler:(void (^)())completionHandler 
     2 {      
     3      /*Store the completion handler. The completion handler is invoked by the view controller's checkForAllDownloadsHavingCompleted method (if all the download tasks have been completed). */
     4       self.backgroundSessionCompletionHandler = completionHandler;  
     5  6 
     7 @end 
     8  
     9 //Session的Delegate 
    10 @implementation APLViewController
    11 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
    12 {
    13     APLAppDelegate *appDelegate = (APLAppDelegate *)
    14     [[UIApplication sharedApplication] delegate];
    15      if (appDelegate.backgroundSessionCompletionHandler) {
    16         void (^completionHandler() = appDelegate.backgroundSessionCompletionHandler;
    17             appDelegate.backgroundSessionCompletionHandler = nil;
    18               completionHandler();
    19     }
    20     NSLog(@"All tasks are finished");
    21 }
    22 @end

    (3)当加入了多个Task,程序切到后台,下载完成了几个Task,然后用户又切换到前台。(程序没有退出)

    切到后台之后,Session的Delegate仍然收不到消息。在下载完成几个Task之后再切换到前台,系统会先汇报已经下载完成的Task的情况,然后继续下载没有下载完成的Task,后面的过程同第一种情况。 

    (4)当加入了多个Task,程序切到后台,几个Task已经完成,但还有Task还没有下载完的时候强制退出程序,然后再进入程序的时候。(程序退出了)

    最后这个情况比较有意思,由于程序已经退出了,后面没有下完Session就不在了后面的Task肯定是失败了。但是已经下载成功的那些Task,新启动的程序也没有听“汇报”的机会了。

    经过实验发现,这个时候之前在NSURLSessionConfiguration设置的NSString类型的ID起作用了,当ID相同的时候,一旦生成Session对象并设置Delegate,马上可以收到上一次关闭程序之前没有汇报工作的Task的结束情况(成功或者失败)。但是当ID不相同,这些情况就收不到了,因此为了不让自己的消息被别的应用程序收到,或者收到别的应用程序的消息,ID还是和程序的Bundle名称绑定上比较好,至少保证唯一性。

  • 相关阅读:
    jsp和servlet的联系与区别
    tomcat会如何处理请求
    java中synchronized的关键字
    mybatis的动态sql
    spring自带的json转换工具,支持@ResponseBody注解
    layer一个web弹窗/层解决方案
    spring mvc出现乱码的问题
    hdu1010 Tempter of the Bone
    hdu 3342 Legal or Not
    ThreadPoolExecutor线程池执行器源码解析
  • 原文地址:https://www.cnblogs.com/xiayao/p/5260227.html
Copyright © 2020-2023  润新知