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

时间:2016-05-23 09:01:43

标签: ios nsurlsession nsurlsessiondownloadtask nsurlsessionconfiguration nsurlsessiontask

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

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

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

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

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session

但是,所有下载完成后都会调用这两种方法。 我放了3个视频供下载,然后将App放到后台。下载了所有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");
}

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

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

2 个答案:

答案 0 :(得分:1)

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

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

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

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

  

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

最后,几年前我创建了一个开源项目,一个NSURLSession的包装器。这个项目是针对iOS 7制作的,所以它可能会使用一些弃用方法,但是这个答案涵盖的部分仍然有效。 Link to FLDownloader

修改 在Rob回答之后,我做了一些检查。似乎处于暂停状态的应用程序与处于被杀死状态的应用程序之间的行为不同。

  • 似乎,当应用程序关闭(被杀死)时,只有在所有下载完成后,系统才会将其唤醒,并调用application:handleEventsForBackgroundURLSession:completionHandler:。我尝试将XCode附加到我的iPhone上,看起来是正确的。
  • 但是,在此link,在“后台转移注意事项”中,似乎如果应用处于“暂停”状态,则表示:
  

如果在您的应用程序被暂停时完成了任何任务,则会调用委托的URLSession:downloadTask:didFinishDownloadingToURL:方法,其中包含与其关联的新下载文件的任务和URL。

修改

如果Apple文档确认,最后一个断言事件似乎是错误的。我亲自检查了iPhone 6S,iOS 9.3.2,XCode和Instruments。我开始了两次下载并关闭了应用程序(暂停状态,由Istruments活动监视器确认 - 进程仍处于活动状态,但没有消耗cpu-time),但未调用URLSession:downloadTask:didFinishDownloadingToURL:方法。但是,当两个下载完成时application:handleEventsForBackgroundURLSession:completionHandler:被调用。

答案 1 :(得分:1)

  

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

只有在完成与该会话相关的所有下载后,才会在handleEventsForBackgroundURLSession后台重新启动应用,而不是逐个下载。后台会话的想法是最大限度地减少在后台保持运行(或重复启动然后暂停)的电池消耗,而是让后台守护程序为您执行此操作,并在完成所有操作时通知您。

理论上,你可能能够实例化一个单独的后台会话,但这会让我觉得滥用后台会话(其目的是减少花费多少时间来启动你的应用并在后台运行它)如果苹果不赞成这种做法,我不会感到惊讶。它还需要一个笨拙的实现(具有多个NSURLSession对象)。

  

如果答案是否定的,那么我的问题是这两种不同回调的目的是什么?是什么将它们彼此分开?

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

顺便说一下,LombaX是正确的,handleEventsForBackgroundURLSession应该启动后台会话。就个人而言,我为completionHandler对象创建了NSURLSession包装器的属性,因此handleEventsForBackgroundURLSession将实例化它(让它准备好调用其委托方法),并保存{{1 }} 那里。它是保存完成处理程序的合理位置,您必须实例化completionHandler及其委托,并且它将NSURLSession保存为不需要返回到应用程序委托以获取保存完成处理程序。

对或错,我的典型实现是使背景NSURLSession对象成为单例。因此我最终得到了类似的东西:

URLSessionDidFinishEventsForBackgroundURLSession

一石二鸟,开始背景NSURLSession,并保存- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler { [BackgroundSession sharedSession].savedCompletionHandler = completionHandler; }