Google Cloud Messaging:iOS App在后台时不会收到提醒 [英] Google Cloud Messaging: don't receive alerts when iOS App is in background

查看:203
本文介绍了Google Cloud Messaging:iOS App在后台时不会收到提醒的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遵循此教程 https://developers.google.com/cloud-messaging / ios / client 在我的iOS应用程序上实现GCM。我的应用服务器是用Java编写的谷歌应用引擎,我使用gcm-server.jar https://github.com / google / gcm 库。我认为我的证书没有问题,我可以注册,获取令牌,甚至可以收到我的应用服务器发送的消息内容。但是,当应用程序处于后台时,我没有收到任何通知提醒,只有当我点击应用程序图标才能重新启动它时,我总是收到它。



我想过那是因为我只实现了 didReceiveRemoteNotification:而不是 didReceiveRemoteNotification:fetchCompletionHandler:,所以我实现了它而不是第一个但我不会在后台接收通知,而且更糟的是,应用程序崩溃时会说出类似于无法识别的选择器发送给实例didReceiveRemoteNotification:的情况,就像userInfo中的某些内容是错误的。我确实允许在xCode中使用背景模式,就像它需要的一样。这里是我使用的代码:

  AppDelegate()

@property(nonatomic,strong)NSDictionary * registrationOptions;
@property(nonatomic,strong)GGLInstanceIDTokenHandler registrationHandler;

@end

@implementation AppDelegate

$ b - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions {

// - 设置通知
[[GCMService sharedInstance] startWithConfig:[GCMConfig defaultConfig]];
if([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
{
NSLog(@Case iOS8);
// iOS 8 Notifications
[applicationUserNotificationSettings:[UIUserNotificationSettings settingsForTypes :( UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)categories:nil]];

[application registerForRemoteNotifications];
}
else
{
NSLog(@Case iOS7);
// iOS< 8通知
[应用程序registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];

$ b $ self.registrationHandler = ^(NSString * registrationToken,NSError * error){
if(registrationToken!= nil){

NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:registrationToken forKey:TOKENGCM];
NSLog(@注册令牌:%@,registrationToken);
//一些代码
}其他{
NSLog(@注册GCM失败,错误:%@,error.localizedDescription);
}
};
返回YES; (UIApplication *)application {
}
$ b - (void)applicationDidEnterBackground:(UIApplication *)application {
[[GCMService sharedInstance] disconnect]; (void)applicationWidEnterForeground:(UIApplication *)application {
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
}

-
//连接GCM服务器以接收非APNS通知
[[GCMService sharedInstance] connectWithHandler:^(NSError * error){
if(error){
NSLog @无法连接到GCM:%@,error.localizedDescription);
} else {
NSLog(@连接到GCM);
// ...
}
}]; (void)applicationWillTerminate:(UIApplication *)application {
}


- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken :( NSData *)deviceToken {
//使用默认配置启动GGLInstanceID共享实例并请求注册
//令牌以接收通知
[[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
self.registrationOptions = @ {kGGLInstanceIDRegisterAPNSOption:deviceToken,
kGGLInstanceIDAPNSServerTypeSandboxOption:@NO};
[[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:SENDER_ID
scope:kGGLInstanceIDScopeGCM
options:self.registrationOptions
handler:self.registrationHandler];

$ b - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@Error in registration。Error:%@,呃);
}

- (void)onTokenRefresh {
//注册令牌轮换正在发生,因此应用程序需要请求新的令牌。
NSLog(@需要更改GCM注册标记。);
[[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:SENDER_ID
scope:kGGLInstanceIDScopeGCM
options:self.registrationOptions
handler:self.registrationHandler];


$ b - (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(@Notification received :%@,userInfo); //如果应用程序处于前台,这将在控制台中打印我的消息内容
UIApplicationState state = [application applicationState];
if(state == UIApplicationStateActive){
NSString * cancelTitle = @Close;
NSString * showTitle = @Show;
NSString * message = [[userInfo valueForKey:@aps] valueForKey:@alert];
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@Some title
message:message
delegate:self
cancelButtonTitle:cancelTitle
otherButtonTitles:showTitle,nil ]。
[alertView show];
}
else {
NSLog(@Not inactive received inactive while);
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:99];
UIAlertView * BOOM = [[UIAlertView alloc] initWithTitle:@BOOM
message:@app is INACTIVE
delegate:self
cancelButtonTitle:@a-ha!
otherButtonTitles:nil];
[BOOM show];
NSLog(@App is not ACTIVE);
[[NSNotificationCenter defaultCenter] postNotificationName:@Notification!
object:nil
userInfo:userInfo];
}
//只有当应用程序启动GCM服务
[[GCMService sharedInstance] appDidReceiveMessage:userInfo];
}

//实现导致无法识别的选择器崩溃
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void(^) (UIBackgroundFetchResult))处理程序{
NSLog(@收到通知:%@,userInfo);
//这只在应用程序启动GCM服务时有效
[[GCMService sharedInstance] appDidReceiveMessage:userInfo];
//处理收到的消息
//调用完成处理程序,传递相应的UIBackgroundFetchResult值
// [START_EXCLUDE]
[[NSNotificationCenter defaultCenter] postNotificationName:@notif
对象:无
userInfo:userInfo];
处理程序(UIBackgroundFetchResultNoData);
// [END_EXCLUDE]
}

@end

有人能够弄清楚我不在前台收到通知吗?



编辑:服务器上使用的Java代码发送GCM消息:

  public static MulticastResult sendViaGCM(String tag,String message,List< String> deviceIdsList)throws IOException {
发件人发件人=新发件人(Constantes.API_KEY);
//此消息对象是Google Cloud Messaging对象
消息msg = new Message.Builder()。addData(tag,tag).addData(message,message).build() ;
MulticastResult result = sender.send(msg,deviceIdsList,5);
返回结果;
}

EDIT2:POST请求的截屏
http://image.noelshack.com/fichiers/2015/34 /1440193492-gcm1.png
http:/ /image.noelshack.com/fichiers/2015/34/1440193502-gcm2.png



EDIT3:请求我现在从我的应用服务器发送:

  public static void sendGCMMessage(String tag,String message,List< String> deviceIdsList){
String request =https://gcm-http.googleapis.com/gcm/send;
尝试{
URL url =新的URL(请求);
HttpURLConnection conn =(HttpURLConnection)url.openConnection();
conn.setDoOutput(true);
//conn.setInstanceFollowRedirects(false);
conn.setRequestMethod(POST);
// Les deux标头必填项:
conn.setRequestProperty(Content-Type,application / json);
conn.setRequestProperty(Authorization,key =+ API_KEY);
// Construction du JSON:
JSONObject fullJSON = new JSONObject();
JSONObject data = new JSONObject();
JSONObject notification = new JSONObject();
data.put(tag,tag);
data.put(message,message);
notification.put(sound,default);
notification.put(badge,1);
notification.put(title,default);
notification.put(body,message);
fullJSON.put(registration_ids,deviceIdsList);

fullJSON.put(通知,通知);
fullJSON.put(content_available,true);
fullJSON.put(data,data);

// Phase finale:
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(fullJSON.toString());
wr.flush();
wr.close(); // pas obligatoire
//conn.setUseCaches(false);
}
catch(Exception e){
e.printStackTrace();


解决方案

基于 GCM文档,您可以设置 content_available true



在iOS上,使用此字段表示APNS有效内容中的可用内容。当发送通知或消息并将其设置为true时,非活动客户端应用程序将被唤醒。在Android上,数据消息默认唤醒应用程序,在Chrome上,当前不支持。 b
$ b

content_available 对应于Apple的 content-available ,您可以找到请参阅此Apple推送通知服务文档



另外,您应该使用 Notification Playload ,用于将消息发送到您的iOS应用程序,以便当您的应用程序处于后台时它可以显示标题。



以下是一个示例HTTP请求:

  https://gcm-http.googleapis.com/gcm/send 
Content-Type:application / json
Authorization:key = API_KEY
{
to:REGISTRATION_TOKEN,
notification:{
sound: default,
badge:1,
title:default,
body:Test,
},
content_available:true,
}

Java库只是一个示例,您可以添加其他字段。例如,在 Message.java 类,您可以添加两个私有变量,一个是 private final Boolean contentAvailable ,另一个是 private final地图< String,String>通知

您可以通过执行 curl -i -HContent-Type:application / json-H来尝试终端中的HTTP请求。 :key = API_KEY-X POST -d{to:REGISTRATION_TOKEN,notificaiton:{sound:default,badge:1,title:default body:test,},content_available:true}'https://android.googleapis.com/gcm/send ,或者在邮差



已编辑:

如果您的应用程序已终止,并且您希望将推送通知显示在您的设备中,则可以设置 hl = en#设置消息优先级>>高优先级(注意,与正常优先级消息相比,将消息设置为高优先级会导致更多的电量消耗
$ b 示例HTTP请求

  { 
to:REGISTRATION_TOKEN,
通知:{
sound:default,
badge:1,
title:default,
body:测试,
},
content_available:true,
priority:normal,
}


I have followed this tutorial https://developers.google.com/cloud-messaging/ios/client to implement GCM on my iOS Application. My app server is a google app engine written in Java and I use the gcm-server.jar https://github.com/google/gcm library. I think my certificates are fine and I can register, get a token and even receive the content of the messages sent by my app server. However, I don't receive any notification alerts when the app is in background, I always receive it only when I click on the app icon to restart it.

I thought that was because I was only implementing didReceiveRemoteNotification: and not didReceiveRemoteNotification:fetchCompletionHandler: so I implemented it instead of the first one but I don't receive notifications while in background either and worse, the app crashes saying something like"unrecognized selector sent to instance didReceiveRemoteNotification:" like something was wrong in the userInfo. I did allow background modes in xCode like required for it. Here is the code I use:

AppDelegate ()

@property (nonatomic, strong) NSDictionary *registrationOptions;
@property (nonatomic, strong) GGLInstanceIDTokenHandler registrationHandler;

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

//-- Set Notification
[[GCMService sharedInstance] startWithConfig:[GCMConfig defaultConfig]];
if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
{
    NSLog(@"Case iOS8");
    // iOS 8 Notifications
    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

    [application registerForRemoteNotifications];
}
else
{
    NSLog(@"Case iOS7");
    // iOS < 8 Notifications
    [application registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
}

self.registrationHandler = ^(NSString *registrationToken, NSError *error){
    if (registrationToken != nil) {

        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:registrationToken forKey:TOKENGCM];
        NSLog(@"Registration Token: %@", registrationToken);
        //some code
    } else {
        NSLog(@"Registration to GCM failed with error: %@", error.localizedDescription);
    }
};
return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
[[GCMService sharedInstance] disconnect];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
// Connect to the GCM server to receive non-APNS notifications
[[GCMService sharedInstance] connectWithHandler:^(NSError *error) {
    if (error) {
        NSLog(@"Could not connect to GCM: %@", error.localizedDescription);
    } else {
        NSLog(@"Connected to GCM");
        // ...
    }
}];
}

- (void)applicationWillTerminate:(UIApplication *)application {
}


- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Start the GGLInstanceID shared instance with the default config and request a registration
// token to enable reception of notifications
[[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
self.registrationOptions = @{kGGLInstanceIDRegisterAPNSOption:deviceToken,
                         kGGLInstanceIDAPNSServerTypeSandboxOption:@NO};
[[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:SENDER_ID
                                                    scope:kGGLInstanceIDScopeGCM
                                                  options:self.registrationOptions
                                                  handler:self.registrationHandler];
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@"Error in registration. Error: %@", err);
}

- (void)onTokenRefresh {
// A rotation of the registration tokens is happening, so the app needs to request a new token.
NSLog(@"The GCM registration token needs to be changed.");
[[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:SENDER_ID
                                                    scope:kGGLInstanceIDScopeGCM
                                                  options:self.registrationOptions
                                                  handler:self.registrationHandler];
}


- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(@"Notification received: %@", userInfo);//This does print the content of my message in the console if the app is in foreground
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
    NSString *cancelTitle = @"Close";
    NSString *showTitle = @"Show";
    NSString *message = [[userInfo valueForKey:@"aps"] valueForKey:@"alert"];
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Some title"
                                                        message:message
                                                       delegate:self
                                              cancelButtonTitle:cancelTitle
                                              otherButtonTitles:showTitle, nil];
    [alertView show];
}
else{
    NSLog(@"Notification received while inactive");
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber: 99];
    UIAlertView *BOOM = [[UIAlertView alloc] initWithTitle:@"BOOM"
                                                   message:@"app was INACTIVE"
                                                  delegate:self
                                         cancelButtonTitle:@"a-ha!"
                                         otherButtonTitles:nil];
    [BOOM show];
    NSLog(@"App was NOT ACTIVE");
   [[NSNotificationCenter defaultCenter] postNotificationName:@"Notification!"
                                                        object:nil
                                                      userInfo:userInfo];
}
// This works only if the app started the GCM service
[[GCMService sharedInstance] appDidReceiveMessage:userInfo];
}

//Implement that causes unrecognized selector crash 
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
NSLog(@"Notification received: %@", userInfo);
// This works only if the app started the GCM service
[[GCMService sharedInstance] appDidReceiveMessage:userInfo];
// Handle the received message
// Invoke the completion handler passing the appropriate UIBackgroundFetchResult value
// [START_EXCLUDE]
[[NSNotificationCenter defaultCenter] postNotificationName:@"notif"
                                                    object:nil
                                                  userInfo:userInfo];
handler(UIBackgroundFetchResultNoData);
// [END_EXCLUDE]
}

@end

Can someone figure out what I don't receive notifications when not in foreground?

EDIT: the Java code used on the server side to send the GCM message:

public static MulticastResult sendViaGCM(String tag, String message, List<String> deviceIdsList) throws IOException {
    Sender sender = new Sender(Constantes.API_KEY);
    // This message object is a Google Cloud Messaging object
    Message msg = new Message.Builder().addData("tag",tag).addData("message", message).build();
    MulticastResult result = sender.send(msg, deviceIdsList, 5);
    return result;
}

EDIT2: screenshots of the POST Request http://image.noelshack.com/fichiers/2015/34/1440193492-gcm1.png http://image.noelshack.com/fichiers/2015/34/1440193502-gcm2.png

EDIT3: the request I now send from my app server:

public static void sendGCMMessage(String tag, String message, List<String> deviceIdsList) {
    String request = "https://gcm-http.googleapis.com/gcm/send";
    try{
        URL url = new URL(request);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        //conn.setInstanceFollowRedirects(false);
        conn.setRequestMethod("POST");
        //Les deux headers obligatoires:
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Authorization", "key=" + API_KEY);
        //Construction du JSON:
        JSONObject fullJSON = new JSONObject();
        JSONObject data=new JSONObject();
        JSONObject notification=new JSONObject();
        data.put("tag", tag);
        data.put("message", message);
        notification.put("sound", "default");
        notification.put("badge", "1");
        notification.put("title", "default");
        notification.put("body", message);
        fullJSON.put("registration_ids", deviceIdsList);

        fullJSON.put("notification", notification);
        fullJSON.put("content_available", "true");
        fullJSON.put("data", data);

        //Phase finale:
        OutputStreamWriter wr= new OutputStreamWriter(conn.getOutputStream());
        wr.write(fullJSON.toString());
        wr.flush();
        wr.close();//pas obligatoire
        //conn.setUseCaches(false);
    }
    catch(Exception e){
        e.printStackTrace();
    }

解决方案

Based on the GCM documentation, you can set the content_available to true.

(On iOS, use this field to represent content-available in the APNS payload. When a notification or message is sent and this is set to true, an inactive client app is awoken. On Android, data messages wake the app by default. On Chrome, currently not supported.)

The content_available is correspond to Apple's content-available, which you can find in this Apple Push Notification Service documentation.

Also, you should use Notification playload, for sending message to your iOS application, so that it can show a banner when your app is in background.

Here's a sample HTTP request:

https://gcm-http.googleapis.com/gcm/send
Content-Type:application/json
Authorization:key=API_KEY
{
   "to" : "REGISTRATION_TOKEN",
   "notification" : {
     "sound" : "default",
     "badge" : "1",
     "title" : "default",
     "body"  : "Test",
   },
   "content_available" : true,
}

The Java library is just a sample, you can add other fields to it. For example, in the Message.java class, you can add two private variables, one is private final Boolean contentAvailable, another one is private final Map<String, String> notification.

You can try a HTTP request in your terminal by doing curl -i -H "Content-Type:application/json" -H "Authorization:key=API_KEY" -X POST -d '{"to":"REGISTRATION_TOKEN", "notificaiton":{"sound":"default", "badge":"1", "title": "default", "body":"test",},"content_available":true}' https://android.googleapis.com/gcm/send, or try it in Postman.

Edited:

If your application was terminated, and you want to the push notifications to be shown in your device, you can set a high priority in your HTTP request body (beware that setting your messages to high priority contributes more to battery drain compared to normal priority messages).

Sample HTTP request:

{
   "to" : "REGISTRATION_TOKEN",
    "notification" : {
     "sound" : "default",
     "badge" : "1",
     "title" : "default",
     "body"  : "Test",
   },
   "content_available" : true,
   "priority" : "normal",
 }

这篇关于Google Cloud Messaging:iOS App在后台时不会收到提醒的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆