Skip to content

Latest commit

 

History

History
91 lines (78 loc) · 5.69 KB

2019-11-19-NSURLSession-Background-Session.md

File metadata and controls

91 lines (78 loc) · 5.69 KB
title date categories tags
NSURLSession - Background Session
2019-11-19 07:09:11 -0800
Tech
iOS

Background SessionNSURLSession中一类特殊的Session。在App处于inactive(包括suspendterminated)的状态时,使用background Session能使我们有能力执行下载任务。事实上,iOS系统是将我们提交的任务放到了另外的进程里执行,待执行完成后,再通知给App,去一个指定的路径下获取结果。

Background Session的使用也不算很复杂。首先我们需要创建一个Background Session

- (NSURLSession *)urlSession {
    static dispatch_once_t onceToken;
    static NSURLSession *_instance = nil;
    dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"MySession"];
        _instance = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
    });
    return _instance;
}

其中identifier是App内的一个唯一标识符,iOS并不要求每个App使用的identifier唯一。

接着需要一个方法启动background task

- (void)startBackgroundTask:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLSessionDownloadTask *task = [[self urlSession] downloadTaskWithURL:url];
    [task resume];
}

�为了处理App的suspend状态,我们需要实现一个application: handleEventsForBackgroundURLSession:completionHandler:代理方法:

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
    [self urlSession];
    self.completionHandler = completionHandler;
}

background task执行完成时,如果App处于inactive状态,iOS系统就会唤起App,并调用这个方法。在这个方法里我们缓存了completionHandler,我们会在稍后用到它。 [self urlSession]是为了重新将Background task和你的App创建的Session关联起来(因为你的App可能已经被terminate了),这很重要,否则系统就不知道这些task是属于哪个Session的。

然后NSURLSessionDelegate的方法URLSessionDidFinishEventsForBackgroundURLSession:会被回调,我们在这里调用刚刚缓存的completionHandler

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    dispatch_async(dispatch_get_main_queue(), ^{
        self.completionHandler();
    });
}

因为回调并不保证在主线程,而completionHandler必须在主线程中调用,所以我们需要把它dispatch到主线程中。

最后,如果下载完成,我们可以在NSURLSessionDownloadDelegate的回调URLSession:downloadTask:didFinishDownloadingToURL:中获取下载的文件。当然你也可以实现URLSession:task:didCompleteWithError:来检查任务是否成功,实现URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:来查看下载进度。 你可以这样使用它们:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    NSLog(@"Did download to %@", location.absoluteString);

    NSString *filename = [location lastPathComponent];

    NSURL *target = [[self targetURL] URLByAppendingPathComponent:filename];
    if ([[NSFileManager defaultManager] fileExistsAtPath:target.path]) {
        [[NSFileManager defaultManager] removeItemAtURL:target error:nil];
    }

    NSError *error;
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:target error:&error];
    if (error) {
        NSLog(@"error occurs while moving file: %@", error);
    }
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    NSLog(@"complete with error: %@", error);
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    NSLog(@"%@: %f", downloadTask, 1.0 * totalBytesWritten / totalBytesExpectedToWrite);
}

Note Background Session可以在App被terminate之后唤起它,但是这仅限于被iOS系统ternimate,如果是用户手动kill,是没办法唤起我们的App的。backgroundSessionConfigurationWithIdentifier:的文档里提到了这一点。

If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and to retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.

Reference

Downloading Files in the Background URLSession downloadTask behavior when running in the background? Downloading files in background with URLSessionDownloadTask