NSURLSession后台传输:从队列中下载的每个视频的回调



我正在使用后台传输服务使用NSURLSession下载多个视频。当应用程序处于后台模式时,下载工作正常,我对此感到满意。我的问题是,我想回调从队列下载的每个视频。

我期望为每个下载的视频调用以下方法:

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler

当系统在后台传输后没有更多消息发送到我们的应用程序时,使用以下方法:

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session

但是,当所有下载完成时,这两种方法都会被调用。我放了3个视频下载,然后把应用程序放在后台。这两种方法都是在所有3个视频下载后调用的。


以下是我在这些方法中所做的:

AppDelegate

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

DownloadViewController

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.backgroundTransferCompletionHandler) 
{
void (^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;
appDelegate.backgroundTransferCompletionHandler = nil;
completionHandler();
}
NSLog(@"All tasks are finished");
}

是否可以在下载每个视频时向用户显示本地通知?或者,我将不得不等到所有视频在后台完成下载?

如果答案是NO,那么我的问题是这两个不同回调的目的是什么?他们之间的区别是什么?

是否可以在下载每个视频时向用户显示本地通知?或者,我将不得不等到所有视频在后台完成下载?

只有当与该会话相关的所有下载都完成时,应用程序才会在后台使用handleEventsForBackgroundURLSession重新启动,而不是逐个下载。后台会话的想法是最大限度地减少在后台运行(或重复启动然后挂起)的电池消耗,而是让后台守护进程为您完成这项工作,并让您知道一切何时完成。

从理论上讲,你可能可以实例化一个单独的后台会话,但这让我觉得是对后台会话的滥用(其目的是减少启动应用程序和在后台运行应用程序的时间),如果苹果不同意这种做法,我也不会感到惊讶。它还需要更笨拙的实现(使用多个NSURLSession对象)。

如果答案是否定的,那么我的问题是这两个不同回调的目的是什么?他们之间的区别是什么?

单独回调的目的是,一旦您的应用程序再次运行,它就可以对每次下载进行所需的任何后期处理(例如,将文件从临时位置移动到最终位置)。每次下载都需要单独的回调,即使在后台模式下重新启动应用程序时,它们都会被快速连续调用。此外,如果应用程序碰巧已经在前台运行,你可以在下载完成时处理单个下载。


顺便说一句,LobaX是正确的,handleEventsForBackgroundURLSession应该启动后台会话。就我个人而言,我将completionHandler作为NSURLSession对象的包装器的属性,因此handleEventsForBackgroundURLSession将实例化它(使它准备好调用其委托方法),并将completionHandler保存在那里。这是保存完成处理程序的逻辑位置,您无论如何都必须实例化NSURLSession及其委托,它可以避免URLSessionDidFinishEventsForBackgroundURLSession需要返回到应用程序委托来获取保存的完成处理程序。

不管对错,我的典型实现是将后台NSURLSession对象作为一个单例。因此,我最终得到了这样的东西:

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

这一举两得,启动了背景NSURLSession,并保存了completionHandler

这里的问题是您使用的是NSURLSessionDelegate,它提供了有关当前下载会话的信息。但是,您希望了解有关单个任务的信息,而不是整个会话的信息。因此,您应该查看NSURLSessionTaskDelegate或NSURLSessionDownloadDelegate

具体来说,使用NSURLSessionDownloadDelegate,您应该实现这种委托方法:

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

如果下载完成时应用程序处于后台模式,则不会自动调用此方法。但是,系统会调用application:handleEventsForBackgroundURLSession:completionHandler:,使您有机会重新构建会话并响应事件(例如,根据您的要求发出通知)更多信息请点击此处:

在iOS中,当后台传输完成或需要凭据时,如果您的应用程序不再运行,则会在后台自动重新启动应用程序,并向应用程序的UIApplicationDelegate发送一个应用程序:handleEventsForBackgroundURLSession:completeHandler:message。此调用包含导致您的应用程序启动的会话的标识符。然后,在创建具有相同标识符的后台配置对象并使用该配置创建会话之前,您的应用程序应该存储该完成处理程序。新创建的会话将自动与正在进行的后台活动重新关联。

上一次,几年前我做了一个开源项目,一个NSURLSession的包装器。这个项目是为iOS 7制作的,所以它可能会使用一些弃用的方法,但这个答案所涵盖的部分仍然有效。FLDownloader 链接

编辑Rob回答后,我做了一些检查。处于暂停状态的应用程序和处于终止状态的应用软件之间的行为似乎有所不同。

  • 似乎,当应用程序关闭(关闭)时,只有在所有下载完成时,系统才会调用application:handleEventsForBackgroundURLSession:completionHandler:将其唤醒。我试着把XCode连接到我的iPhone上,它似乎是正确的
  • 然而,在这个链接的"后台转移注意事项"中,如果应用程序处于"暂停"状态,它似乎会说:

如果在您的应用程序挂起时完成了任何任务,则会使用该任务和与之关联的新下载文件的URL调用代理的URLSession:downloadTask:didFinishDownloadingToURL:方法。

编辑

最后一个断言,如果苹果文档证实了这一事件,似乎是错误的。我亲自检查了iPhone 6S、iOS 9.3.2、XCode和Instruments。我启动了两次下载并关闭了应用程序(挂起状态,由Istruments活动监视器确认-进程仍然有效,但没有占用cpu时间),但没有调用URLSession:downloadTask:didFinishDownloadingToURL:方法。然而,当两个下载都完成时,调用了application:handleEventsForBackgroundURLSession:completionHandler:

最新更新