gpt4 book ai didi

ios - AFNetworking 和后台传输

转载 作者:IT老高 更新时间:2023-10-28 11:33:20 32 4
gpt4 key购买 nike

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

我看到了 WWDC 705 - What's New in Foundation Networking session ,他们演示了在应用程序终止甚至崩溃后继续进行后台下载。

这是使用新的 API application:handleEventsForBackgroundURLSession:completionHandler: 完成的,并且 session 的委托(delegate)最终将获得回调并可以完成其任务。

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

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

或者我在这里遗漏了一些东西......

让我解释一下我的意思:

例如,我的应用程序是一个照片消息应用程序,假设我有一个 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];

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

如何通过后台传输来实现这一点?不会调用此完成 block ,因此我无法更新 newPhotoMsg

最佳答案

一些想法:

  1. 您必须确保完成Handling iOS Background Activity 中列出的必要编码。 URL 加载系统编程指南 部分说:

    If you are using NSURLSession in iOS, your app is automatically relaunched when a download completes. Your app’s application:handleEventsForBackgroundURLSession:completionHandler: app delegate method is responsible for recreating the appropriate session, storing a completion handler, and calling that handler when the session calls your session delegate’s URLSessionDidFinishEventsForBackgroundURLSession: method.

    该指南显示了您可以做什么的一些示例。坦率地说,我认为 WWDC 2013 视频 What’s New in Foundation Networking 后半部分讨论的代码示例更清晰。

  2. 如果应用只是挂起,AFURLSessionManager 的基本实现将与后台 session 一起工作(假设您已经在网络任务完成时看到调用 block )完成上述)。但正如您所猜测的,如果应用程序终止或崩溃,任何传递给您创建 NSURLSessionTask 上传和下载的方法的特定于任务的 block 参数都会丢失。”

    对于后台上传,这是一个烦恼(因为您在创建任务时指定的任务级信息进度和完成 block 不会被调用)。但是,如果您使用 session 级别的再现(例如 setTaskDidCompleteBlocksetTaskDidSendBodyDataBlock),它将被正确调用(假设您在重新实例化 session 管理器时始终设置这些 block )。

    事实证明,这个丢 block 的问题其实对于后台下载来说问题更大,但是那里的解决方案非常相似(不要使用基于任务的 block 参数,而是使用基于 session 的 block ,例如setDownloadTaskDidFinishDownloadingBlock)。

  3. 另一种选择,您可以坚持使用默认(非背景)NSURLSession,但如果用户在执行任务时离开应用程序,请确保您的应用程序请求一点时间来完成上传正在处理。例如,在您创建 NSURLSessionTask 之前,您可以创建一个 UIBackgroundTaskIdentifier:

    UIBackgroundTaskIdentifier __block taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void) {
    // handle timeout gracefully if you can

    [[UIApplication sharedApplication] endBackgroundTask:taskId];
    taskId = UIBackgroundTaskInvalid;
    }];

    但是要确保网络任务的完成 block 正确地通知iOS它已经完成了:

    if (taskId != UIBackgroundTaskInvalid) {
    [[UIApplication sharedApplication] endBackgroundTask:taskId];
    taskId = UIBackgroundTaskInvalid;
    }

    这不如后台 NSURLSession 强大(例如,您的可用时间有限),但在某些情况下这可能很有用。


更新:

我想我应该添加一个如何使用 AFNetworking 进行后台下载的实际示例。

  1. 首先定义你的后台管理器。

    //
    // 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
  2. 确保应用委托(delegate)保存完成处理程序(根据需要实例化后台 session ):

    - (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;
    }
  3. 然后开始下载:

    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];
    }

    注意,我不提供任何与任务相关的 block ,因为这些 block 在后台 session 中不可靠。 (即使在应用程序终止并且这些 block 早已消失后,后台下载也会继续进行。)必须依赖于 session 级,仅可轻松重新创建 setDownloadTaskDidFinishDownloadingBlock

显然这是一个简单的示例(只有一个后台 session 对象;只是使用 URL 的最后一个组件作为文件名将文件保存到 docs 文件夹等),但希望它说明了这种模式。

关于ios - AFNetworking 和后台传输,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21350125/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com