iOS 8 推送通知操作按钮 - 当应用程序在后台时,handleActionWithIdentifier 中的代码并不总是运行

Posted

技术标签:

【中文标题】iOS 8 推送通知操作按钮 - 当应用程序在后台时,handleActionWithIdentifier 中的代码并不总是运行【英文标题】:iOS 8 push notification action buttons - code in handleActionWithIdentifier does not always run when app is in background 【发布时间】:2015-04-25 18:56:08 【问题描述】:

我在 ios 8 上的推送通知中添加了两个操作按钮:一个 Accept 按钮和一个 Deny 按钮。这两个按钮都不会打开应用程序,但会根据按下的按钮发出不同的服务器请求。这是我的设置:

+ (void)requestForPushNotificationToken 
    UIApplication *application = [UIApplication sharedApplication];
    // if ios 8 or greater
    if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) 
        UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];
        [acceptAction setActivationMode:UIUserNotificationActivationModeBackground];
        [acceptAction setTitle:@"Accept"];
        [acceptAction setIdentifier:@"ACCEPT_ACTION"];
        [acceptAction setDestructive:NO];
        [acceptAction setAuthenticationRequired:NO];

        UIMutableUserNotificationAction *denyAction = [[UIMutableUserNotificationAction alloc] init];
        [denyAction setActivationMode:UIUserNotificationActivationModeBackground];
        [denyAction setTitle:@"Deny"];
        [denyAction setIdentifier:@"DENY_ACTION"];
        [denyAction setDestructive:NO];
        [denyAction setAuthenticationRequired:NO];

        UIMutableUserNotificationCategory *actionCategory = [[UIMutableUserNotificationCategory alloc] init];
        [actionCategory setIdentifier:@"ACTIONABLE"];
        [actionCategory setActions:@[acceptAction, denyAction]
                        forContext:UIUserNotificationActionContextDefault];

        NSSet *categories = [NSSet setWithObject:actionCategory];

        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert) categories:categories];
        [application registerUserNotificationSettings:settings];
     else if ([application respondsToSelector:@selector(registerForRemoteNotificationTypes:)])  // ios 7 or lesser
        UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
        [application registerForRemoteNotificationTypes:myTypes];
    

然后,在我的委托方法中,我指定当用户按下某个操作按钮时要执行的操作:

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler 
    if ([identifier isEqualToString:@"ACCEPT_ACTION"]) 
        // Sending a request to the server here
    
    else if ([identifier isEqualToString:@"DENY_ACTION"]) 
        // Sending a request to the server here
    

    if (completionHandler) 
        completionHandler();
    

理想的场景是用户全程不需要启动应用;按AcceptDeny 将对服务器进行不同的调用。使用上面的代码,我看到按钮操作的行为非常不稳定:

很多时候,当应用程序处于后台并且根本没有进行任何服务器调用时,动作处理程序中的代码不会执行;发生这种情况时,如果我点击我的应用程序图标并启动我的应用程序,则处理程序代码将在启动应用程序时立即运行。 有时,处理程序代码被触发并且一切正常。只要我按下其中一个操作按钮,就会发出服务器请求。 如果我在 Xcode 中设置断点并单步执行处理程序代码,成功率也是 100%。我不需要启动我的应用程序,按下按钮时会执行处理程序代码。

谁能帮我找出导致这种不稳定行为的原因?提前致谢。

【问题讨论】:

º您好,您可以共享通知的有效负载吗?当远程通知到来时,我无法设置操作按钮...>. @SophySwicz 在我的服务器代码中相关的有效负载是payloadBuilder.category("ACTIONABLE"); 您需要添加ACTIONABLE 类别 最后,我的有效载荷结束于:"aps":"alert":"loc-key":"KEY_BODY_ALERT_VOTE_MORNING","category":"ACTIO‌​NABLE"并且在设置 UIMutableUserNotificationCategory 时使用 UIUserNotificationActionContextDefault 【参考方案1】:

我终于找到原因了。它有时有效,有时无效,这一事实应该更早地给我提示。

根据application:handleActionWithIdentifier:forRemoteNotification:completionHandler:的Apple文档:

此方法的实现应执行与指定标识符相关的操作,并在完成后立即执行 completionHandler 参数中的块。在实现结束时未能执行完成处理程序块将导致您的应用被终止。

我在application:handleActionWithIdentifier:forRemoteNotification:completionHandler 方法的末尾调用完成处理程序。但是,我在处理程序代码中向服务器发送请求这一事实意味着我的 end of implementation 不仅仅是在方法的末尾;我真正的目的在于我的请求的回调。我编码的方式,完成处理程序和回调在两个不同的线程上,当完成处理程序在它到达回调之前运行时,它会失败。

所以解决方法是将完成处理程序移动到请求的回调方法中,即真正的“实现结束”。像这样的:

[MyClient sendRequest:userInfo withSuccessBlock:^(id responseObject)
    NSLog(@"Accept - Success");
    if (completionHandler) 
        completionHandler();
    
 withFailureBlock:^(NSError *error, NSString *responseString) 
    NSLog(@"Accept - Failure: %@",[error description]);
    if (completionHandler) 
        completionHandler();
    
];

【讨论】:

【参考方案2】:

在 iOS8 上你需要调用

[application registerForRemoteNotifications];

来自application:didRegisterUserNotificationSettings:

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings 
    [application registerForRemoteNotifications];

【讨论】:

我已经这样做了,抱歉我没有粘贴那些代码。推送通知在所有其他情况下都有效,只是操作按钮处理程序有问题。

以上是关于iOS 8 推送通知操作按钮 - 当应用程序在后台时,handleActionWithIdentifier 中的代码并不总是运行的主要内容,如果未能解决你的问题,请参考以下文章

在后台处理应用程序时的 iOS 推送通知

当用户通过向上滑动强制退出应用程序时,可操作的推送通知不会出现在 iOS 8+ 设备上

iOS推送通知横幅清除操作按钮[重复]

当应用程序被杀死时,在推送通知后执行操作:react-native,IOS

IOS;当应用程序未运行或在后台时,推送通知不起作用

ios在后台处理推送通知