问题 在应用程序以应用程序为前提之前,异步代码不会执行:didReceiveRemoteNotification:fetchCompletionHandler:


在我们的应用程序中,我们希望下载少量数据以响应推送通知。到目前为止,推送通知工作正常,在后台启动应用程序并导致调用didReceiveRemoteNotification。

问题是,在这个方法返回之后,应用程序不会再获得CPU时间,直到它再次被预先考虑,因此没有机会在后台异步获取该数据。

将此简化为最简单的情况,我仍然无法运行异步代码。

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    [application setApplicationIconBadgeNumber:1];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [application setApplicationIconBadgeNumber:9];

        completionHandler(UIBackgroundFetchResultNewData);
    });
}

响应推送,应用程序在后台启动,徽章计数设置为1,但在从主屏幕启动应用程序之前,徽章编号不会设置为9。

在调用完成处理程序之前,iOS是否应该继续运行应用程序,最多30秒?

Info.plist指定了远程通知后台模式,推送有效负载包含'content-available':'1',并且我没有通过在应用切换器中向上滑动来退出应用。

要添加,我们使用Parse使用以下Javascript发送此推送通知:

Parse.Push.send({
    where: installationQuery,
    data: {
        "content-available": 1,
    }
}, { success: function() {},
    error: function(error) {}
});

2745
2017-08-16 03:52


起源

你试过这个: stackoverflow.com/questions/16295953/... ? - anhtu


答案:


先拿一个 这里 并确保您启用了推送通知并添加了内容可用字段:

使用推送通知启动下载   如果您的服务器在您的应用有新内容时向用户的设备发送推送通知,您可以要求系统在后台运行您的应用,以便它可以立即开始下载新内容。此背景模式的目的是最小化用户看到推送通知与应用程序能够显示关联内容之间经过的时间。应用程序通常会在用户看到通知的大致相同的时间唤醒,但仍然比您提供的时间更长。   要支持此后台模式,请从Xcode项目的“功能”选项卡的“后台模式”部分启用“远程通知”选项。 (您还可以通过在应用程序的Info.plist文件中包含带有远程通知值的UIBackgroundModes键来启用此支持。)   对于触发下载操作的推送通知,通知的有效负载必须包含内容可用密钥,其值设置为1.当存在该密钥时,系统会在后台唤醒应用程序(或将其启动到后台)并且调用app delegate的应用程序:didReceiveRemoteNotification:fetchCompletionHandler:method。您对该方法的实施应下载相关内容并将其集成到您的应用中。   下载任何内容时,建议您使用NSURLSession类来启动和管理下载。有关如何使用此类管理上载和下载任务的信息,请参阅URL加载系统编程指南。

接下来,有没有理由你使用“dispatch_after”延迟2秒?

有可能因为你在运行循环结束时调用“dispacth_after”iOS“认为”没有待处理的工作要做并将进程置于休眠状态,因此在调度块时没有人正在监听它。

用“dispatch_async”替换它可能会解决这个问题。

最后,如果你确实需要延迟,你应该告诉iOS你需要一些时间在后台,像这样 -

    UIApplication *application = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier __block backgroundTaskId = [application beginBackgroundTaskWithExpirationHandler:^{
    if (backgroundTaskId != UIBackgroundTaskInvalid) {
        [application endBackgroundTask:backgroundTaskId];
        backgroundTaskId = UIBackgroundTaskInvalid;
    }
}];

然后做你的背景工作。 不要忘记工作完成后结束任务。打电话给像 -

if (backgroundTaskId != UIBackgroundTaskInvalid) {
        [application endBackgroundTask:backgroundTaskId];
        backgroundTaskId = UIBackgroundTaskInvalid;
    }

10
2017-08-19 07:18



使用后台任务就是它! dispatch_after 2秒只是为了将问题减少到最简单的可能情况。实际上,我们将使用dispatch_async。但是这里的主要问题是iOS似乎假设我们在主runloop完成其循环时完成了执行,而不是在调用fetch完成处理程序时。通过使用后台任务,iOS为我们提供了运行和执行所需异步操作的时间。 - Aaron Thompson