通过 NSURLSession / NSURLSessionDownloadTask 下载完成时发送本地通知

Posted

技术标签:

【中文标题】通过 NSURLSession / NSURLSessionDownloadTask 下载完成时发送本地通知【英文标题】:Send local notification when download completes through NSURLSession / NSURLSessionDownloadTask 【发布时间】:2015-07-29 20:18:46 【问题描述】:

我在NSURLSession 上使用NSURLSessionDownloadTask 对象,以允许用户在应用处于后台/设备锁定时下载文档。我还想通过本地通知通知用户个别下载已完成。

为此,我在 -URLSession:downloadTask:didFinishDownloadingToURL: 下载任务委托方法中触发本地通知,但是我想知道是否有更好的地方添加触发通知的代码,因为 Apple 解释它的方式,下载任务将被传递给系统,由此我得出结论,一旦(或不久之后)应用程序后台运行,下载任务的委托将不再调用这些委托。

我的问题:添加触发本地通知的代码的最佳位置是什么?有没有人有过在他们的应用程序中添加这种功能的经验?

【问题讨论】:

我在试验中注意到的一些事情:当多个文件排队等待下载时,通知要么合并并在所有下载结束时发送,要么并非所有文件都被触发。我还尝试在-application:handleEventsForBackgroundURLSession:completionHandler: 中添加通知触发代码,但 似乎 (从我到目前为止所做的测试中)只触发一次通知,而不是针对已下载的每个文档. 【参考方案1】:

您的问题的答案可以在 Apple 文档URL Loading System Programming Guide 中找到:

ios 中,当后台传输完成或需要凭据时, 如果您的应用不再运行,iOS 会自动重新启动您的 后台应用程序并调用 application:handleEventsForBackgroundURLSession:completionHandler: 应用程序的 UIApplicationDelegate 对象上的方法。此调用提供 导致您的应用程序启动的会话的标识符。 您的应用程序应该存储该完成处理程序,创建一个背景 具有相同标识符的配置对象,并创建一个会话 使用该配置对象。新会话自动 与正在进行的后台活动重新关联。后来,当会话 完成最后一个后台下载任务,它发送会话 委托URLSessionDidFinishEventsForBackgroundURLSession: 消息。 然后,您的会话委托应调用存储的完成处理程序。

如果在您的应用暂停期间完成了任何任务,委托的 然后URLSession:downloadTask:didFinishDownloadingToURL: 方法 使用任务和新下载文件的 URL 调用 相关联。

如您所见,它比设置delegate 对象要复杂得多。通过委托方法,只有当应用程序处于前台模式时才会通知您。在其他情况下(应用程序处于后台模式,应用程序被终止)您需要处理上面引用中描述的 AppDelegate 方法。

Apple 还提供example project,它展示了如何处理后台下载/上传任务。此示例将帮助您找到放置“本地通知”代码的位置。

【讨论】:

尽管文档说每次任务完成时都会调用application:handleEventsForBackgroundURLSession:completionHandler,但我发现情况并非如此,它实际上是在整个会话完成时调用一次。对此有何想法? @Andrei 并不是说​​每次任务完成时都会调用它。它表示如果在任务完成时应用程序被终止,则将调用此方法。当所有其他任务完成时,应用程序可能处于后台。在这种情况下,这个方法只会被调用一次。 感谢您的快速回复。知道我应该如何找出一项任务何时完成 @Andrei,据我了解,如果在应用程序终止时任务完成,您需要处理此调用 application:handleEventsForBackgroundURLSession:completionHandler。在所有其他情况下,您需要处理 NSURLSession 委托方法。此外,Apple 文档中描述的所有逻辑都必须在您的应用中实现。【参考方案2】:

正如 Visput 上面解释的那样,一旦下载完成,就会调用这个方法。application:handleEventsForBackgroundURLSession:completionHandler:

如果您将 NSURLSessionConfiguration 类与 backgroundSessionConfiguraton 一起使用,则会发生这种情况。你可能错过了那部分。

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.BGTransfer"];
sessionConfiguration.HTTPMaximumConnectionsPerHost = 5; // To set the max concurrent connections

详细解释here。

【讨论】:

【参考方案3】:

正如@Gautam Jain 所建议的,你必须使用backgroundSessionConfiguration 来实现你的目标。下面我附上了一个例子,希望它对你有所帮助

DownloadModel.h

#import "AppDelegate.h"
@interface DownloadModel : NSObject<NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDownloadDelegate>
NSString *resp;


+(instancetype)shared;
-(NSURLSessionDownloadTask *) downloadTaskWithURL:(NSURL*)url ;
@end

下载模型.m

#import "DownloadModel.h"
@interface DownloadModel ()
@property (strong,nonatomic) NSURLSession *downloadSession;
@end

@implementation DownloadModel
+(instancetype)shared
static dispatch_once_t onceToken;
static DownloadModel *downloader=nil;

dispatch_once(&onceToken, ^

    downloader=[DownloadModel new];
);

return downloader;



-(id)init
self=[super init];
if(self)

    NSURLSessionConfiguration *downloadConfig=[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"DownloadDemo"];
    //        downloadConfig.timeoutIntervalForRequest = 30;
    //        downloadConfig.timeoutIntervalForResource = 30;
    //        downloadConfig.HTTPMaximumConnectionsPerHost = 1;
    //        downloadConfig.sessionSendsLaunchEvents=YES;
    downloadConfig.allowsCellularAccess = YES;
    downloadConfig.networkServiceType = NSURLNetworkServiceTypeBackground;
    //        downloadConfig.discretionary = YES;
    self.downloadSession=[NSURLSession sessionWithConfiguration:downloadConfig delegate:self delegateQueue:nil];
    self.downloadSession.sessionDescription=@"Video Downloader";



return self;


-(NSURLSessionDownloadTask *) downloadTaskWithURL:(NSURL*)url
return [self.downloadSession downloadTaskWithURL:url];


#pragma mark download delegate

在此方法中使用通知或本地通知

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

[[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadFinish" object:downloadTask userInfo:nil];

下载进度

- (void)URLSession:(NSURLSession *)session downloadTask(NSURLSessionDownloadTask *)downloadTask didWriteData(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

CGFloat progress=(CGFloat)totalBytesWritten/totalBytesExpectedToWrite;

NSDictionary *userInfo=@@"progress":@(progress);

[[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadProgress" object:downloadTask userInfo:userInfo];






#pragma mark delegate
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
AppDelegate *appdelegate=[[UIApplication sharedApplication] delegate];

if(appdelegate.backgroundSessionCompletionHandler)
    appdelegate.backgroundSessionCompletionHandler();
    appdelegate.backgroundSessionCompletionHandler=nil;


@end

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (copy ,nonatomic) void(^backgroundSessionCompletionHandler)();

@end

AppDelegate.m

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

self.backgroundSessionCompletionHandler=completionHandler;

[DownloadModel shared];

ViewController.m 调用此方法-(NSURLSessionDownloadTask *) downloadTaskWithURL:(NSURL*)url

- (void)viewDidLoad  

//Add Notification observers to track download progress and call the above method

[DownloadModel shared] downloadTaskWithURL:url];

 

别忘了启用后台提取

【讨论】:

后台下载不需要添加“Background Fetch”。

以上是关于通过 NSURLSession / NSURLSessionDownloadTask 下载完成时发送本地通知的主要内容,如果未能解决你的问题,请参考以下文章

通过 React Native 使用 must-revalidate 和 NSURLSession

使用NSURLSession同步获取数据(通过添加信号量)

NSURLSession 通过 httpproxy 和 /etc/hosts 时出现 -1001 错误

如何通过 AFNetworking 和 NSUrlSession 调用 SOAP Web 服务

我想通过 Swift 实现 `curl --header $1`。(NSURLSession 的标头)

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