AF网络和后台传输

对于如何利用新的iOS 7 NSURLSession后台传输功能和AFNetworking(版本2和3),我有些困惑。


我看到了WWDC 705 - What’s New in Foundation Networking会话,他们演示了后台下载,该下载在应用终止甚至崩溃后仍然继续。


这是使用新的API application:handleEventsForBackgroundURLSession:completionHandler:以及会话的委托人最终将获得回调并可以完成其任务的事实来完成的。


因此,我想知道如何将其与AFNetworking一起使用(如果可能)以继续在后台下载。


问题是,AFNetworking方便地使用基于块的API来执行所有请求,但是如果应用终止或崩溃,这些块也将消失。那么我该如何完成任务呢?


也许我在这里想念什么...


让我解释一下我的意思:


例如,我的应用程序是一个照片消息传递应用程序,可以说我有一个PhotoMessage代表一条消息的对象,并且该对象具有诸如


state -描述照片下载的状态。

resourcePath -最终下载的照片文件的路径。

因此,当我从服务器收到新消息时,我创建了一个新PhotoMessage对象,并开始下载其照片资源。


PhotoMessage *newPhotoMsg = [[PhotoMessage alloc] initWithInfoFromServer:info];

newPhotoMsg.state = kStateDownloading;


self.photoDownloadTask = [[BGSessionManager sharedManager] downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {

    NSURL *filePath = // some file url

    return filePath;

} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {

    if (!error) {

        // update the PhotoMessage Object

        newPhotoMsg.state = kStateDownloadFinished;

        newPhotoMsg.resourcePath = filePath;

    }

}];


[self.photoDownloadTask resume];   

如您所见,我根据完成PhotoMessage的响应使用完成块来更新该对象。


我该如何通过后台传输来实现?此完成程序块将不会被调用,因此,我无法更新newPhotoMsg。


jeck猫
浏览 806回答 3
3回答

慕码人8056858

一些想法:您必须确保执行“ URL加载系统编程指南”的“ 处理iOS后台活动”部分中概述的必要编码:如果您NSURLSession在iOS 中使用,则下载完成后会自动重新启动您的应用。应用程序的application:handleEventsForBackgroundURLSession:completionHandler:应用程序委托方法负责重新创建适当的会话,存储完成处理程序,并在会话调用您的会话委托人的URLSessionDidFinishEventsForBackgroundURLSession:方法时调用该处理程序。该指南显示了您可以执行的一些示例。坦率地说,我觉得在WWDC 2013视频的后半部分讨论的代码样本中的新增基础网络都显得更加清晰。AFURLSessionManager如果仅暂停了应用程序,则的基本实现将与后台会话配合使用(假设您已完成上述操作,则在完成网络任务时,您会看到调用的块)。但是,正如您猜到的那样,“如果应用终止或崩溃” ,传递给AFURLSessionManager您创建NSURLSessionTask用于上载和下载的方法的特定于任务的块参数都将丢失。对于后台上传,这很烦人(因为在创建任务时指定的任务级信息进度和完成块将不会被调用)。但是,如果您使用会话级再现(例如setTaskDidCompleteBlock和setTaskDidSendBodyDataBlock),则会被正确调用(假设在重新实例化会话管理器时始终设置这些块)。事实证明,丢失块的问题实际上对于后台下载而言更成问题,但是那里的解决方案非常相似(不要使用基于任务的块参数,而要使用基于会话的块,例如setDownloadTaskDidFinishDownloadingBlock)。另一种选择是,您可以使用默认设置(非后台)NSURLSession,但是如果用户在执行任务时离开应用程序,请确保您的应用程序请求一点时间来完成上传。例如,在创建之前NSURLSessionTask,您可以创建一个UIBackgroundTaskIdentifier:UIBackgroundTaskIdentifier __block taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void) {    // handle timeout gracefully if you can    [[UIApplication sharedApplication] endBackgroundTask:taskId];    taskId = UIBackgroundTaskInvalid;}];但是,请确保网络任务的完成块正确通知iOS它已完成:if (taskId != UIBackgroundTaskInvalid) {    [[UIApplication sharedApplication] endBackgroundTask:taskId];    taskId = UIBackgroundTaskInvalid;}这没有背景那么强大NSURLSession(例如,您有有限的可用时间),但是在某些情况下这可能很有用。更新:我以为我会添加一个实际的示例,说明如何使用AFNetworking进行后台下载。首先定义您的后台经理。////  BackgroundSessionManager.h////  Created by Robert Ryan on 10/11/14.//  Copyright (c) 2014 Robert Ryan. All rights reserved.//#import "AFHTTPSessionManager.h"@interface BackgroundSessionManager : AFHTTPSessionManager+ (instancetype)sharedManager;@property (nonatomic, copy) void (^savedCompletionHandler)(void);@end和////  BackgroundSessionManager.m////  Created by Robert Ryan on 10/11/14.//  Copyright (c) 2014 Robert Ryan. All rights reserved.//#import "BackgroundSessionManager.h"static NSString * const kBackgroundSessionIdentifier = @"com.domain.backgroundsession";@implementation BackgroundSessionManager+ (instancetype)sharedManager {    static id sharedMyManager = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        sharedMyManager = [[self alloc] init];    });    return sharedMyManager;}- (instancetype)init {    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kBackgroundSessionIdentifier];    self = [super initWithSessionConfiguration:configuration];    if (self) {        [self configureDownloadFinished];            // when download done, save file        [self configureBackgroundSessionFinished];   // when entire background session done, call completion handler        [self configureAuthentication];              // my server uses authentication, so let's handle that; if you don't use authentication challenges, you can remove this    }    return self;}- (void)configureDownloadFinished {    // just save the downloaded file to documents folder using filename from URL    [self setDownloadTaskDidFinishDownloadingBlock:^NSURL *(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location) {        if ([downloadTask.response isKindOfClass:[NSHTTPURLResponse class]]) {            NSInteger statusCode = [(NSHTTPURLResponse *)downloadTask.response statusCode];            if (statusCode != 200) {                // handle error here, e.g.                NSLog(@"%@ failed (statusCode = %ld)", [downloadTask.originalRequest.URL lastPathComponent], statusCode);                return nil;            }        }        NSString *filename      = [downloadTask.originalRequest.URL lastPathComponent];        NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];        NSString *path          = [documentsPath stringByAppendingPathComponent:filename];        return [NSURL fileURLWithPath:path];    }];    [self setTaskDidCompleteBlock:^(NSURLSession *session, NSURLSessionTask *task, NSError *error) {        if (error) {            // handle error here, e.g.,            NSLog(@"%@: %@", [task.originalRequest.URL lastPathComponent], error);        }    }];}- (void)configureBackgroundSessionFinished {    typeof(self) __weak weakSelf = self;    [self setDidFinishEventsForBackgroundURLSessionBlock:^(NSURLSession *session) {        if (weakSelf.savedCompletionHandler) {            weakSelf.savedCompletionHandler();            weakSelf.savedCompletionHandler = nil;        }    }];}- (void)configureAuthentication {    NSURLCredential *myCredential = [NSURLCredential credentialWithUser:@"userid" password:@"password" persistence:NSURLCredentialPersistenceForSession];    [self setTaskDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *credential) {        if (challenge.previousFailureCount == 0) {            *credential = myCredential;            return NSURLSessionAuthChallengeUseCredential;        } else {            return NSURLSessionAuthChallengePerformDefaultHandling;        }    }];}@end确保应用程序委托保存完成处理程序(根据需要实例化后台会话):- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {    NSAssert([[BackgroundSessionManager sharedManager].session.configuration.identifier isEqualToString:identifier], @"Identifiers didn't match");    [BackgroundSessionManager sharedManager].savedCompletionHandler = completionHandler;}然后开始下载:for (NSString *filename in filenames) {    NSURL *url = [baseURL URLByAppendingPathComponent:filename];    NSURLRequest *request = [NSURLRequest requestWithURL:url];    [[[BackgroundSessionManager sharedManager] downloadTaskWithRequest:request progress:nil destination:nil completionHandler:nil] resume];}请注意,我不提供任何与任务相关的块,因为它们与后台会话不可靠。(即使在应用终止后,背景下载仍会继续进行,并且这些功能块早已消失。)人们必须依靠会话级别,并且setDownloadTaskDidFinishDownloadingBlock只能轻松地重新创建。显然,这是一个简单的示例(仅一个后台会话对象;仅使用URL的最后一个组件作为文件名将文件保存到docs文件夹中;等等),但希望它能说明这种模式。

宝慕林4294392

回调是否为块应该没有任何区别。实例化an时AFURLSessionManager,请确保使用实例化它NSURLSessionConfiguration backgroundSessionConfiguration:。此外,请确保调用经理setDidFinishEventsForBackgroundURLSessionBlock与你的回调块-这就是你应该写通常NSURLSessionDelegate的方法定义的代码: URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session。此代码应调用您的应用程序委托的后台下载完成处理程序。关于后台下载任务的一则建议-即使在前台运行时,它们的超时也将被忽略,这意味着您可能会在没有响应的下载中“卡住”。这没有记录在任何地方,使我疯了一段时间。第一个嫌疑人是AFNetworking,但即使直接调用NSURLSession之后,其行为也保持不变。祝好运!

狐的传说

AFURLSessionManagerAFURLSessionManager创建和管理的NSURLSession基于指定的对象上NSURLSessionConfiguration的对象,它符合<NSURLSessionTaskDelegate>,<NSURLSessionDataDelegate>,<NSURLSessionDownloadDelegate>,和<NSURLSessionDelegate>。链接到此处的文档
打开App,查看更多内容
随时随地看视频慕课网APP