在后台使用 NSURLSession 逐个下载 100 个文件的列表

Posted

技术标签:

【中文标题】在后台使用 NSURLSession 逐个下载 100 个文件的列表【英文标题】:Downloading a list of 100 files one by one using NSURLSession in Background 【发布时间】:2014-04-29 05:23:39 【问题描述】:

我已经使用NSURLSession 实现了一个针对 ios 7+ 的下载管理器应用程序。下载管理器有一个按优先级顺序下载的文件的排队列表。当应用程序处于后台并且代理调用被正确调用时,下载工作正常。但是当应用程序进入后台时,即使下载完成也需要太多时间

NSURLSession delegate:- **URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL

被调用。有时根本不调用委托,当我来到前台时,会调用下载任务委托。这种延迟有什么原因吗?

【问题讨论】:

我也面临同样的问题。我正在传递一组项目,并且仅在我重新启动应用程序时才会触发委托方法。 回调不一致,下载开始时间太长。我用模拟器试了一下小文件,下载了一些小图像文件。下载完成后的回调正在触发2 或 3 分钟前完成。这会导致开始下载下一个文件的延迟。一旦我将应用程序置于后台,就会出现问题。在前台,相同的功能运行良好。 您是否遵循developer.apple.com/library/ios/documentation/Cocoa/Conceptual/…(后台传输注意事项)中的所有说明?我的意思是,您使用后台会话配置并在 appDelegate 和 NSURLSessionDelegate 中实现所有必要的回调? 是的,我使用相同的功能。当我首先进入后台时,第一个文件回调发生得很快,但当它移动到第 3 和第 4 个文件时,它变得太慢了。 我认为清除 URLSessionDidFinishEventsForBackgroundURLSession 中的完成处理程序会导致问题。 【参考方案1】:

我遇到了一个非常相似的问题,后台任务会启动但随后似乎会暂停。当应用程序回到前台时,任务就会完成。

我通过记录-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 的输出验证了这种情况

我发现解决这个问题的方法是与你如何存储、处理和执行你的完成处理程序有关。

在我的例子中,这个过程开始于

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

[self.fmStore performBackgroundRefresh:^(UIBackgroundFetchResult result) 

    //Set application badge if new data is available
    if (result==UIBackgroundFetchResultNewData) [UIApplication sharedApplication].applicationIconBadgeNumber++;
    completionHandler(result);

];

远程通知开始下载过程的位置。

管理下载的方法根据新数据的可用性返回一个值

-(void)performBackgroundRefresh:(void (^)(UIBackgroundFetchResult))completion
   if(newData) completion(UIBackgroundFetchResultNewData);
    else completion(UIBackgroundFetchResultNoData);

此时它返回到存储完成处理程序的 ApplicationDelegate

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
//Store completion handler for background session
self.sessionCompletionHandler=completionHandler;

最后,这段代码被执行,它调用完成处理程序并创建适当的通知

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
[session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) 
    if (![downloadTasks count]) 


        FM_AppDelegate *appDelegate=(FM_AppDelegate *)[[UIApplication sharedApplication] delegate];
        if (appDelegate.sessionCompletionHandler) 
            void (^completionHandler)() = appDelegate.sessionCompletionHandler;
            appDelegate.sessionCompletionHandler = nil;
            completionHandler();
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^
            [[NSNotificationCenter defaultCenter] postNotificationName:@"ContentRefreshNotification" object:Nil];
        ];
    
];
 

这个来回过程发生在我的例子中,因为 NSURLSession 存在于作为 ApplicationDelegate 属性的对象中。如果您将 NSURLSession 实现为 ApplicationDelegate 本身的属性,那么所有这些代码都将存在于同一个文件中。

希望这会有所帮助,但如果您需要更多信息,请参阅这两个教程 1 和 2,因为我的代码基于我在这些教程中阅读的内容。

【讨论】:

@Saddiq Jaffer 在 WWDC 2014 的 NSURLSession 视频中,Apple 建议不要使用逐个下载,而是下载整个文件。 这正是我在方法-(void)performBackgroundRefresh:(void (^)(UIBackgroundFetchResult))completion 中所做的。我的示例中没有显示下载代码,因为我只是想演示如何处理和调用完成处理程序。

以上是关于在后台使用 NSURLSession 逐个下载 100 个文件的列表的主要内容,如果未能解决你的问题,请参考以下文章

NSURLSession 后台下载 - 通过网络故障恢复

iOS NSURLSession 实现网络请求-文件下载-上传-后台下载

NSURLSession 后台下载不起作用

NSURLSession 后台传输不起作用

NSURLSession,数据任务转换为下载任务后,后台无法下载

NSURLSession 后台任务:避免重复