NSURLSession是iOS7中新的網絡接口。
NSURLSession提供的功能:
- 通過URL将資料下載下傳到記憶體
- 通過URL将資料下載下傳到檔案系統
- 将資料上傳到指定URL
- 在背景完成上述功能
NSURLSession工作流程:
1.建立一個NSURLSessionConfiguration,用于第二步建立NSSession時設定工作模式和網絡配置。
工作模式:
1.一般模式(default):這實際上與NSURLConnection的網絡協定棧是一樣,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。
2.及時模式(ephemeral):沒有持久性存儲的緩存,Cookie或證書。這對于實作像秘密浏覽功能的功能來說,是很理想的。
3.背景模式(background):獨特之處在于,它會建立一個背景會話。背景回話不同于正常的,普通的會話,它甚至可以在應用程式挂起,退出,崩潰的情況下運作上傳和下載下傳任務。初始化時指定的辨別符,被用于向任何可能在程序外恢複背景傳輸的守護程序提供上下文。方法中identifier參數指定了會話的ID,用于标記背景的session。
NSURLSessionConfiguration有兩個屬性:
1.allowsCellularAccess屬性指定是否允許使用蜂窩連接配接。
2.dscretionary屬性為YES時表示當程式在背景運作時由系統自己選擇最佳的網絡連接配接配置,該屬性可以節省通過蜂窩連接配接的帶寬。
在使用背景傳輸資料的時候,建議使用discretionary屬性,而不是allowsCellularAccess屬性,因為它會把WiFi和電源可用性考慮在内。補充:這個标志允許系統為配置設定任務進行性能優化。這意味着隻有當裝置有足夠電量時,裝置才通過Wifi進行資料傳輸。如果電量低,或者隻僅有一個蜂窩連接配接,傳輸任務是不會運作的。背景傳輸總是在discretionary模式下運作。
2.建立一個NSURLSession,系統提供了兩個建立方法:
sessionWithConfiguration:
sessionWithConfiguration:delegate:delegateQueue:
3.建立一個NSURLRequest
4.調用NSURLSession對象提供的Task函數,建立一個NSURLSessionTask。
根據職能不同Task有三種子類:
1.NSURLSessionUploadTask:上傳用的Task,傳完以後不會再下載下傳傳回結果。
2.NSURLSessionDownloadTask:下載下傳用的Task。
3.NSURLSessionDataTask:可以上傳内容,上傳完後傳回再進行下載下傳。(NSURLSessionDataTask 和 NSURLSessionDownloadTask 繼承自NSURLSession,NSURLSessionUploadTask 繼承自NSURLSessionDataTask)
5.當不再需要連接配接調用Session的invalidateAndCancel直接關閉,或者調用finishTasksAndInvalidate等待目前Task結束後關閉。這時Delegate會收到URLSession:didBecomeInvalidWithError:這個事件。Delegate收到這個事件之後會被解引用。
6.如果是一個BackgroundSession,在Task執行的時候,使用者切到背景,Session會和ApplicationDelegate做互動。當程式切換到背景後,在BackgroundSession中的Task還會繼續下載下傳。
��1: 資料任務NSURLSessionDataTask:
// 建立請求路徑
NSString *urlStr = @"http://www.baidu.com/thcgi/get_todo_list?uid=3&oid=1";
NSURL *url = [NSURL URLWithString:urlStr];
// 建立請求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 建立資料任務
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
// 不是在主線程執行
NSLog(@"%@", dict);
}];
[task resume];
��2:上傳任務NSURLSessionUploadTask:
// 建立請求路徑
NSString *urlStr = @"http://www.baidu.com/thcgi/get_todo_list";
NSURL *url = [NSURL URLWithString:urlStr];
// 建立請求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 建立參數字元串
NSString *parmStr = @"uid=3&oid=1";
// 将參數字元串轉成NSData類型
NSData *parmData = [parmStr dataUsingEncoding:NSUTF8StringEncoding];
// 建立上傳任務
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:parmData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 不是在主線程執行
}];
[uploadTask resume];
��3:下載下傳任務NSURLSessionDownloadTask:
// 下載下傳任務也需要一個請求,但不同之處在于它們的completionHandler。資料和上傳任務在完成時立即傳回,但下載下傳任務将資料寫入本地的臨時檔案。completionHandler有責任将檔案從它的臨時位置移動到一個永久位置。
// 建立請求路徑
NSString *urlStr = @"http://p1.pichost.me/i/40/1639665.png";
NSURL *url = [NSURL URLWithString:urlStr];
// 建立請求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 下載下傳任務
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 不是在主線程
// location: 是一個臨時位置,如果需要将下載下傳的檔案永久儲存,需要将檔案從它的location臨時位置移動到一個永久位置。
NSData *data = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:data];
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(10, 100, 100, 100)];
iv.image = image;
iv.backgroundColor = [UIColor redColor];
[self.view addSubview:iv];
});
}];
[downloadTask resume];
��4:斷點續傳
- (void)start:(UIButton *)btn
{
NSLog(@"Start download task");
// 用NSURLSession和NSURLRequest建立網絡任務
_task = [[self session] downloadTaskWithRequest:[self request]];
[_task resume];
}
- (void)pause:(UIButton *)btn
{
NSLog(@"Pause download task");
if (_task) {
// 取消下載下傳任務,把已下載下傳資料存起來
[_task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
_partialData = resumeData;
_task = nil;
}];
}
}
- (void)stop:(UIButton *)btn
{
NSLog(@"Stop download task");
if (!_task) {
// 判斷是否有已下載下傳資料,有的話就斷點續傳,沒有就完全重新下載下傳
if (_partialData) {
_task = [[self session] downloadTaskWithResumeData:_partialData];
} else {
_task = [[self session] downloadTaskWithRequest:[self request]];
}
}
[_task resume];
}
// 建立session
- (NSURLSession *)session
{
// 建立NSURLSession
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
// delegateQueue: 決定NSURLSessionDownloadDelegate代理的方法是在哪個線程中執行
// 如果delegateQueue設定為[NSOperationQueue mainQueue],則NSURLSessionDownloadDelegate代理的方法是在主線程中執行
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
return session;
}
// 建立請求
- (NSURLRequest *)request
{
// 建立請求
NSURL *url = [NSURL URLWithString:@"http://p1.pichost.me/i/40/1639665.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
return request;
}
// 建立檔案本地儲存目錄,将檔案儲存在documents檔案夾下
- (NSURL *)createDirectoryForDownloadItemFromURL:(NSURL *)location
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsDirectory = urls[0];
return [documentsDirectory URLByAppendingPathComponent:[location lastPathComponent]];
}
// 把檔案拷貝到指定目錄
- (BOOL)copyTempFileAtURL:(NSURL *)location toDestination:(NSURL *)destination
{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:destination error:NULL];
[fileManager copyItemAtURL:location toURL:destination error:&error];
if (error == nil) {
return true;
} else {
NSLog(@"%@", error);
return false;
}
}
#pragma mark - NSURLSessionDownloadDelegate
// 下載下傳完成後調用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
// 下載下傳成功後,檔案是儲存在一個臨時目錄的,需要開發者自己拷到放置該檔案的目錄
NSLog(@"Download success for URL: %@", location.description);
NSURL *destination = [self createDirectoryForDownloadItemFromURL:location];
BOOL success = [self copyTempFileAtURL:location toDestination:destination];
if (success) {
// 檔案儲存成功後,使用GCD調用主線程把圖檔檔案顯示在UIImageView中
UIImage *image = [UIImage imageWithContentsOfFile:[destination path]];
_imageView.image = image;
_imageView.contentMode = UIViewContentModeScaleAspectFit;
_imageView.hidden = NO;
} else {
NSLog(@"Meet error when copy file");
}
_task = nil;
}
// 每次接收到伺服器資料時調用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
// bytesWritten:每次下載下傳資料的容量大小
// totalBytesWritten:已經下載下傳資料的容量大小
// totalBytesExpectedToWrite:正在下載下傳的檔案總容量大小
// 重新整理進度條的delegate方法,同樣地,擷取資料,調用主線程重新整理UI
double currentProgress = totalBytesWritten/(double)totalBytesExpectedToWrite;
_progressView.progress = currentProgress;
_progressView.hidden = NO;
}
// 恢複下載下傳時調用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
// fileOffset:已經下載下傳資料的容量大小
// expectedTotalBytes:正在下載下傳的檔案總容量大小
}