让 GCM 在后台为 iOS 设备工作

Making GCM work for iOS device in the background

我正在尝试为 IOS 和 Android 客户端使用 GCM。当应用程序在前台时,IOS 似乎可以正常工作,但是,当应用程序在后台时,通知中心不会收到消息并且 didReceiveRemoteNotification with completionHandler 不会被调用。

我发现一个问题是从 GCM 到 APNS 的消息格式错误。也就是说,它看起来是这样的:

[message: New message, collapse_key: do_not_collapse, from: **************]
同时,IOS 推送通知应该在通知中包含 aps 键,对吗?以及content-available设置为1。例如:

{ "aps":{ "content-available":1 }, "data-id" : 345 }

顺便说一句,前台应用程序无论如何都会收到消息,问题只出在后台。关于我应该如何解决问题的任何建议,以使 GCM 适用于 ios 和 android?

更新: 这是我在网上找到的:

Regarding actual communication, as long as the application is in the background on an iOS device, GCM uses APNS to send messages, the application behaving similarly as using Apple’s notification system. But when the app is active, GCM communicates directly with the app

所以我在前台模式下收到的消息:

[message: New message, collapse_key: do_not_collapse, from: **************]

是GCM的直接消息(APNS根本没有参与这件事)。所以问题是:APNS 是否会重新格式化 GCM 发送给它的内容以遵守 ios 通知格式?如果是这样,我怎么知道 APNS 实际上做了什么以及它是否以不同的格式向我发送通知?有什么方法可以查看来自 APNS 的传入数据日志?

更新: 好的,我设法改变了消息的结构,现在在前台模式下我收到了以下消息:

Notification received: ["aps": {"alert":"Simple message","content-available":1}, collapse_key: do_not_collapse, from: **************]

现在好像格式化好了,但是app在后台还是没有任何反应。 didReceiveRemoteNotification completionHandler 没有被调用!我应该寻找什么以及问题出在哪里?方括号会成为推送通知的问题吗?更准确地说,ios 不会 post 来自该传入通知的任何 alerts/badges/banners。

献给每一个想寻找 GCM 背景之谜答案的可怜人。我解决了,问题出在格式上。我发布了正确的格式以及 Java 将 Http 请求与一些消息一起发送到 GCM 所需的代码。 所以Http请求头中应该有两个字段,即:

Authorization:key="here goes your GCM api key"
Content-Type:application/json for JSON data type

那么消息正文应该是一个 json 字典,键为 "to" 和 "notification"。例如:

{
  "to": "gcm_token_of_the_device",
  "notification": {
    "sound": "default",
    "badge": "2",
    "title": "default",
    "body": "Test Push!"
  }
}

这是使用 GCM 向指定设备发送推送的简单 java 程序(仅使用 java 库):

public class SendMessage {

    //config
    static String apiKey = ""; // Put here your API key
    static String GCM_Token = ""; // put the GCM Token you want to send to here
    static String notification = "{\"sound\":\"default\",\"badge\":\"2\",\"title\":\"default\",\"body\":\"Test Push!\"}"; // put the message you want to send here
    static String messageToSend = "{\"to\":\"" + GCM_Token + "\",\"notification\":" + notification + "}"; // Construct the message.

    public static void main(String[] args) throws IOException {
        try {

            // URL
            URL url = new URL("https://android.googleapis.com/gcm/send");

            System.out.println(messageToSend);
            // Open connection
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            // Specify POST method
            conn.setRequestMethod("POST");

            //Set the headers
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Authorization", "key=" + apiKey);
            conn.setDoOutput(true);

            //Get connection output stream
            DataOutputStream wr = new DataOutputStream(conn.getOutputStream());

            byte[] data = messageToSend.getBytes("UTF-8");
            wr.write(data);

            //Send the request and close
            wr.flush();
            wr.close();

            //Get the response
            int responseCode = conn.getResponseCode();
            System.out.println("\nSending 'POST' request to URL : " + url);
            System.out.println("Response Code : " + responseCode);

            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();

            //Print result
            System.out.println(response.toString()); //this is a good place to check for errors using the codes in http://androidcommunitydocs.com/reference/com/google/android/gcm/server/Constants.html

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

尝试根据此处的文档在您的负载中设置优先级密钥:https://developers.google.com/cloud-messaging/concept-options#setting-the-priority-of-a-message

  • 普通优先级在 APNs 术语中等于 5
  • 高优先级在 APNs 术语中等于 10

此处您可以在此处获得有关 Apple APN 优先级及其行为的更多信息: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/APNsProviderAPI.html

有效负载示例:

{
  "to": "gcm_device_token",
  "priority": "high",
  "content_available": false,
  "notification": {
    "sound": "default",
    "badge": "1",
    "title": "Push Title",
    "body": "Push Body"
  }
}

重要的是要注意,在 iOS 设备上,如果您使用 GCM 的应用程序已被终止(在应用程序切换器中滑动),那么您的设备只有在收到消息时才会唤醒,前提是您使用"notification"、"content_available" 和 "priority"(设置为 "high")。如果你有一个或另一个,它可能会在应用程序被杀死时起作用。但是一旦应用程序被终止,您必须在通知有效负载中包含所有这 3 个密钥。

像这样:

{
    "to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification": {
        "title": "test",
        "body": "my message"
    },
    "priority": "high",
    "content_available": true
}
  1. 是为 C# 做的,认为这可能是 help.I 也有这个问题 将 content_available 添加到 true 后,它起作用了。根据 Apple 文档,这就是 OS 理解的方式 应用程序在后台时的通知。

        JObject notification =new Object(
        new JProperty("to","put token which you get from running client application "),   
        new JProperty("content_available",true),
        new JProperty("priority","high"),
        new JProperty("notification",new JObject(
        new JProperty("title","message"),
        new JProperty("body","test message")
         ))
        );
    

使用 nodeJS 和 node-gcm npm 库我发现以下有效负载对我有效 iOS,对于 Android 我发送的有效负载略有不同,因为我想要在系统托盘中显示之前拦截所有推送通知:

{ dryRun: false,
  data: 
   { customKey1: 'CustomValue1',
     customKey2: 'CustomValue2',
     content_available: '1',
     priority: 'high' },
  notification: 
   { title: 'My Title',
     icon: 'ic_launcher',
     body: 'My Body',
     sound: 'default',
     badge: '2' } }

当然,您需要确保您的 iOS 应用可以处理入站通知,但这应该在您的应用处于后台时通过。