iOS 后台任务未完成

iOS Background Task Not Completing

我知道可以实施后台任务来执行某些需要超过分配的 3-4 秒的进程 applicationDidEnterBackground 让您使用类似这样的东西:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{

        // Clean up your background stuff 

        NSLog(@"Finsihed background task.");
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Begin your background stuff...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // Do something...like update Parse DB
        NSLog(@"Doing something...");
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    });
}

不幸的是,我似乎在这里遗漏了什么。当我收到 Doing something... 时,从上面的代码中可以明显看出,我从未收到任何表明后台任务过期或进入 expirationHandler 以正确完成任务的指示。

首先我很困惑 "do something" dispatch 是如何与后台任务相关的,或者与任务相关联以让应用程序知道允许此过程在应用程序大约 10 分钟内完成允许 BG 任务。

注意:我已确保在我的 info.plist 中允许 Background Modes,因为许多已发布的解决方案指出可能是其他人的问题。

如果有人可以帮助我正确地完成后台任务,我将不胜感激。

仅供参考:这样做的主要目的是使用查询更新我的Parse数据库,以将用户新的(设备本地的)值设置到数据库.如果我不够清楚,这就是我会使用的:

// Do something...like update Parse DB
NSLog(@"Doing something...");
. . .
PFQuery *query = [PFQuery queryWithClassName:@"_User"];
// Retrieve the object by id
[query getObjectInBackgroundWithId:<SOME_OBJECT_ID> block:^(PFObject *userInfo, NSError *error) {
    // Save user info to current user info on Parse
    [userInfo saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"Could not update Parse with error: %@", error);
        }
        else {
            NSLog(@"Updated Parse successfully.");
        }
   }];
}];

更新: 我试图在 Parse 块中调用 [application endBackgroundTask:bgTask]; 但它没有被调用。有几次它确实假设这两次都是最快的,但我正在尝试处理成功更新 Parse 将比关闭应用程序花费更长的时间的情况(除了那些 2 之外的所有情况)。这是我所做的:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    if ([[UIDevice currentDevice] isMultitaskingSupported]) {
        NSLog(@"Good for you.");
    }

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    self.backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId];
        NSLog(@"done");
    }];

    // Begin your background stuff...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // Do something...like update Parse DB
        NSLog(@"Doing something...");

        PFQuery *query = [PFQuery queryWithClassName:@"_User"];
        // Retrieve the object by id
        [query getObjectInBackgroundWithId:<SOME_OBJECT_ID> block:^(PFObject *userInfo, NSError *error) {
            // Save user info to current user info on Parse
            userInfo[@"test"] = [NSNumber numberWithInteger:12345];

            [userInfo saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
                if (error != nil) {
                    NSLog(@"Could not update Parse with error: %@", error);
                    [application endBackgroundTask:self.backgroundTaskId];
                    self.backgroundTaskId = UIBackgroundTaskInvalid;
                }
                else {
                    NSLog(@"Updated Parse successfully.");
                    [application endBackgroundTask:self.backgroundTaskId];
                    self.backgroundTaskId = UIBackgroundTaskInvalid;
                }
            }];
        }];
    });
}

除了您从 dispatch_async 块外部调用 [application endBackgroundTask:self.backgroundTaskId]; 之外,您的结构通常是正确的,因此后台任务将在 Parse 操作完成之前结束。此外,如果您正在更新的用户是当前登录的用户,您可以使用 PFUser.currentUser 而不必搜索。

所以,你的 applicationDidEnterBackground 应该是这样的:

- (void)applicationDidEnterBackground:(UIApplication *)application {

    self.bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{

        // Clean up your background stuff

        NSLog(@"Expired background task.");
        [application endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];

    // Begin your background stuff...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSLog(@"Doing something...");

        // Do something...like update Parse DB
        PFUser *user=[PFUser currentUser];
        // Update User fields as required
        [user saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
            if (succeeded) {
                NSLog(@"Saved user");
            } else {
                NSLog(@"Error:%@",error.localizedDescription);
            }
            NSLog(@"Background time remaining=%f",UIApplication.sharedApplication.backgroundTimeRemaining);
            [application endBackgroundTask:self.bgTask];
            self.bgTask = UIBackgroundTaskInvalid;
        }];


    });
}