title | date | categories | tags | |
---|---|---|---|---|
NSURLSession - Background Session |
2019-11-19 07:09:11 -0800 |
Tech |
|
Background Session
是NSURLSession
中一类特殊的Session
。在App处于inactive
(包括suspend
和terminated
)的状态时,使用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.
Downloading Files in the Background URLSession downloadTask behavior when running in the background? Downloading files in background with URLSessionDownloadTask