GCM 推送通知未传送到设备

GCM Push Notifications are not delivered to devices

我们(Panos 和 Rainer - 请参阅下面的评论)有一台服务器和几台 Android 设备。

我们想通过 GCM 从我们的服务器向 Android 台设备发送推送通知。

现在我们向 GCM 服务器发出 post 请求。 GCM 服务器响应是一切正常(成功==1 甚至是消息 ID)! 但是推送通知永远不会传送到设备。

如果我们使用相同的数据和 Chrome 插件 Postman - 通知会立即发送。

我们尝试了很多不同的解决方案。我们总是得到 GCM 服务器的反馈,一切正常 - 但推送通知未发送。

我们也试过这个: https://github.com/googlesamples/google-services/blob/master/android/gcm/gcmsender/src/main/java/gcm/play/android/samples/com/gcmsender/GcmSender.java

This 是用于 Postman 请求的数据,可以正常工作。 Rainer 已经提到我们在 Java 端尝试了几种实现,似乎我们总是能够与服务通信并收到到目前为止看起来正确的响应:

{
    "multicast_id":7456542468425129822,
    "success":1,
    "failure":0,
    "canonical_ids":0,
    "results":
    [{
        "message_id":"0:1457548597263237%39c590d7f9fd7ecd"
    }]
} 

不确定我是否在正确的轨道上,但你是指下游 HTTP 消息(纯文本)吗?

尝试将以下 JSON 发送到服务(来自 Postman),这再次导致了肯定响应,但这次通知没有到达设备(只是为了说明这一点,目前有设备上没有应用程序主动侦听传入通知 -> 首先我们只想确保它们通常到达设备):

{ 
   "data": 
   {
      "score": "5x1",
      "time": "15:10"
   },
   "to" : "SECRET-DEVICE-TOKEN"
}

感谢所有试图在这里提供帮助的人,但老实说,这个问题真的很令人沮丧。与似乎无法 return 有用响应的 interface\service 进行通信,以防请求包含可能最终阻止 GCM 向设备发送推送通知的恶意内容,感觉就像一个痛苦屁股。如果 Postman 也会失败我会说好吧,你不能这么傻:-)

这是我们已经使用过的一些快速但肮脏的实现。

  1. 例子

        try 
        {
           URL url = new URL(apiUrl);
           HttpsURLConnection conn = (HttpsURLConnection);//also tried HttpURLConnection
           url.openConnection();
           conn.setDoOutput(true);
           conn.setRequestMethod("POST");
           conn.setRequestProperty("Content-Type", "application/json");
           conn.setRequestProperty("Authorization", "key="+apiKey);
    
           conn.setDoOutput(true);
    
           String json = "{\"priority\":\"high\",\"notification\":{\"title\":\"Some title\",\"text\":\"Some text\"},\"to\":\""+deviceToken+"\"}";
    
           OutputStream os = conn.getOutputStream();
           os.write(json.getBytes());
           os.flush();
       }
       catch(Exception exc)
       {
         System.out.println("Error while trying to send push notification: "+exc);
       }
    
  2. 例子

    HttpClient httpClient = HttpClientBuilder.create().build(); 
    
    try 
    {
        HttpPost request = new HttpPost(apiUrl);
        StringEntity params =new StringEntity("{\"priority\":\"high\",\"notification\":{\title\":\"Some title\",\"text\":\"Some text\"},\"to\":\""+deviceToken+"\"}");
        request.addHeader("Content-Type", "application/json");
        request.addHeader("Authorization", "key="+apiKey);
        request.setEntity(params);
        HttpResponse response = httpClient.execute(request);
    
        // check response
        System.out.println(response.getStatusLine().toString());
    }catch (Exception exc) {
        System.out.println("Error while trying to send push notification: "+exc);
    } finally {
        httpClient.getConnectionManager().shutdown(); //Deprecated
    }
    
  3. 例子

    try
    {
        String charset = "UTF-8"; 
        URLConnection connection = new URL(apiUrl).openConnection();
        connection.setDoOutput(true);
        connection.setRequestProperty("Accept-Charset", charset);
        connection.setRequestProperty("Content-Type", "application/json;charset=" + charset);
        connection.setRequestProperty("Authorization", "key="+apiKey);
    
        String param = "{\"priority\":\"high\",\"notification\":{\"title\":\"Some title\",\"text\":\"Some text\"},\"to\":\""+deviceToken+"\"}";
        try (OutputStream output = connection.getOutputStream()) 
        {
            output.write(param.getBytes(charset));
        }
    
        InputStream response = connection.getInputStream();
    }
    catch(Exception exc)
    {
        System.out.println("Error while trying to send push notification: "+exc);
    }
    
  4. 例子

    try 
    {
        // prepare JSON
        JSONObject jGcmData = new JSONObject();
        JSONObject jData = new JSONObject();
    
        jData.put("message", "{ \"data\": {\"score\": \"5x1\",\"time\": \"15:10\"},\"to\" : \""+deviceToken+"\"}");
    
        jGcmData.put("to", deviceToken);
    
        jGcmData.put("data", jData);
    
        // Create connection to send GCM Message request.
        URL url = new URL("https://android.googleapis.com/gcm/send");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestProperty("Authorization", "key=" + apiKey);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
    
        // Send GCM message content.
        OutputStream outputStream = conn.getOutputStream();
        outputStream.write(jGcmData.toString().getBytes());
    
        // Read GCM response.
        InputStream inputStream = conn.getInputStream();
        String resp = IOUtils.toString(inputStream);
        System.out.println(resp);
     } catch (IOException e) {
        System.out.println("Unable to send GCM message. "+e);
    }
    

您也可以 post 您使用的 URL。有一个新的 GCM enpoint,如下所示:

https://gcm-http.googleapis.com/gcm/send

我还不确定是什么导致了您这边的问题。但是以下内容已经过测试并且可以正常工作:

public class Main {

    public static void main(String[] args) {
    // write your code here

        try {

            String url = "https://gcm-http.googleapis.com/gcm/send";

            URL obj = new URL(url);
            HttpsURLConnectionImpl conn = (HttpsURLConnectionImpl) obj.openConnection();


            conn.setRequestProperty("Content-Type", "application/json");
            conn.setDoOutput(true);

            conn.setRequestMethod("POST");
            conn.setRequestProperty ("Authorization", "key=***");

            String title = "Short title";
            String body = "A body :D";
            String token = "****";
            String data =  "{ \"notification\": { \"title\": \"" + title +"\", \"body\": \"" + body + "\" }, \"to\" : \"" + token + "\", \"priority\" : \"high\" }";
            OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
            out.write(data);
            out.close();

            String text = getText(new InputStreamReader(conn.getInputStream()));
            System.out.println(text);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getText(InputStreamReader in) throws IOException {
        StringBuilder sb=new StringBuilder();
        BufferedReader br = new BufferedReader(in);
        String read;
        while((read=br.readLine()) != null) {
            sb.append(read);
        }
        br.close();
        return sb.toString();
    }
}

迈克,您的示例也适用于我们这边。在将您的实现与我们这边的实现进行比较后,我发现唯一真正的区别是使用的 URL!! 在我们的 Java 实现中使用的 URL 不知何故是 https://android.googleapis.com/gcm/send

似乎 https://gcm-http.googleapis.com/gcm/send 是正确的,顺便说一下,它也用于我们的 Postman 测试。

但是,为什么我们失败的测试中的 URL 仍然有效并且 returns 有回应!?

在 json 中将优先级设置为高解决了我的问题。

'registration_ids' => $id,
'priority' => 'high',
'data' => $load

对于我们的案例,客户端 Android 设备存在间歇性互联网连接问题,即网络中断,从而导致通知传递失败。我们使用以下 JAVA GCM 代码解决了可靠性问题:

gcmPayload.setTime_to_live(messageExpiryTime); //in seconds. Set notification message expiry to give user time to receive it in case they have intermittent internet connection, or phone was off
gcmPayload.setPriority("high");

和 APNS 代码:

ApnsService apnsService = APNS.newService().withCert(certificateStream, configurations.getApnPassword()).withProductionDestination().build();
PayloadBuilder payloadBuilder = APNS.newPayload();
...
payloadBuilder.instantDeliveryOrSilentNotification(); //same as content-available=true
String payload = payloadBuilder.build();

Integer now =  (int)(new Date().getTime()/1000);
//use EnhancedApnsNotification to set message expiry time
for(String deviceToken : deviceTokens) {
    EnhancedApnsNotification notification = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID() /* Next ID */,
        now + messageExpiryTime /* Expiry time in seconds */,
        deviceToken /* Device Token */,
        payload);
        apnsService.push(notification);
}

此外,如果您的后端服务器时间与客户端移动应用程序时间不同,请记住考虑时区。