谷歌云消息推送 - 有时消息直到网络状态改变才被接收

40
在开发一个与GCM集成的小项目时,我遇到了一个有点奇怪的问题。有时候当我开始查看日志以查看是否接收到消息时,消息似乎没有传输过来,直到我改变了网络状态(也就是说,最初在WiFi上,如果我关闭WiFi并移动到移动数据,消息会到达正常)。一旦我改变了网络状态,消息就开始正常到达,而一旦我将网络状态改回之前的状态(此处为WiFi),消息就继续被接收。
该项目本身包括从启动时便启动(在启动时启动GCMBaseIntentService),这也能够正常工作,我确信该应用程序/服务正在运行,因为当出现此问题时,我已经手动启动了应用程序(它还检查服务是否正在运行,如果没有,则运行并检查其是否已注册)。
是否有其他人遇到过这个问题或者有任何指针可以解决这个问题?在消息未被接收和接收到消息之间(更改网络状态后),我在日志中看不到什么有用的信息。我已经阅读了GCM文档,并没有看到任何关于由于超时(在设备本身上)导致消息未被接收或任何可能影响此问题的配置选项。
非常感谢您的帮助 - 如果需要,我可以提供源代码,尽管它几乎没有偏离android-sdk提供的演示应用程序。

2
是的,我看到了类似的情况,其他人也有。在Wi-Fi连接时,收据是即时的,在3G网络下则可能有可变延迟。将飞行模式切换开关打开,然后关闭可以使消息出现。我在Google开发者组中添加了一条评论(目前无法找到!)。我的设备是三星Galaxy S2。 - NickT
1
嗨,链接是 https://groups.google.com/forum/?fromgroups=#!topic/android-gcm/bsYumo68fsc。我的手机是 O2 的。我总会最终收到消息,但最多可能需要 30 分钟。我在伦敦这里的信号很强(通常为 H+)。 - NickT
我不相信被接受的答案总是正确的。在我们办公室的几台设备上,我们有可靠的WiFi和HSDPA连接,我可以重现这个问题。重新连接WiFi总是会传递推送消息,任何后续消息都要等到再次重新连接才能到达。您能否提供一些关于项目设置的见解?这是一个库项目吗?您的应用程序是否从库项目中调用GCM处理?您的应用程序在哪些情况下注册GCM,并且它是否注销过? - Paul Lammertsma
2
我知道这篇文章很旧,但我确认我也遇到了与你描述的GCM完全相同的问题。在一些设备上,在长时间运行应用程序而没有设备重启的情况下,它会发生。它似乎是系统范围内的:其他应用程序也停止接收推送通知。打开/关闭WiFi开关会再次传递推送通知。因此,也许一个解决方案是定期以编程方式切换WiFi。 - Michael Pedersen
仍然发生在2016年9月,使用FCM 9.4.0。 - Tim
显示剩余3条评论
5个回答

39

我也注意到了这一点。虽然我没有深入研究实际的代码,但以下是我对这种情况发生的理解。

GCM(以及大多数推送消息服务)通过保持与Google推送通知服务器的长连接来工作。该套接字通过在电话和服务器之间发送“心跳”消息来保持打开状态。

偶尔,网络状态可能会更改,导致此套接字断开连接(例如从3g切换到wifi设备的IP地址发生变化)。如果消息在重新建立套接字之前到达,则设备将无法立即收到消息。

只有当手机尝试发送心跳消息时,它才会注意到套接字已经断开,然后进行重新连接。

再次强调,这只是我对它如何工作以及为什么会发生的基本理解,我可能会有所错误。


接受了你的答案,因为目前似乎没有解决方案,但你的答案最好地描述了问题的根源。 - Seidr
你说得对,GCM无法很好地处理TCP超时问题,更多信息请参见:https://dev59.com/bWQn5IYBdhLWcg3wiXqd#18428357 - andQlimax

21

导致GCM消息延迟的原因有很多。如果在您更改网络状态或开/关飞行模式后开始接收消息,则最可能的原因是网络关闭连接而未发送FIN / RST。

GCM维护着一条长期存在的连接,并在知道连接已断开时进行重新连接。 路由器/AP/NAT应该发送FIN或RST来终止TCP连接,以便GCM和服务器知道连接已死亡。

然而,许多路由器和移动运营商并没有这样做,此时GCM需要依赖心跳包,Wifi上大约15分钟,移动上更长。电池寿命/网络使用与心跳频率之间存在权衡。


对于这个问题,你提供了很好的技术扩展。 - Seidr
GCM维护长期连接,并在知道连接已断开的情况下重新连接。不确定这个解释是否正确。即使GCM服务器确定连接已断开,也没有办法从服务器发起新连接。因为客户端设备通常位于NAT后面,没有全局白色IP地址。只有客户端能够发起新连接。所以我们唯一能做的就是等待新的心跳包来批准新连接并保持其活动状态。 - rzlvmp

1
根据您在上面答案中的评论和我使用GCM推送通知的经验,如果网络(互联网连接)可用,您应该能够收到推送通知。我总是在运行推送通知应用程序之前检查互联网连接的可用性,例如像这样尝试检查,如果这是真的,您应该能够收到推送通知。
    private boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager 
          = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null;
}

0

所以,如果@theelfismike的想法是正确的,我可以使用类似以下的东西:

  ConnectivityManager cm = (ConnectivityManager) context
            .getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    if (null != activeNetwork) {
        if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
           // here the network changed to Wifi so I can send a heartbeat to GCM to keep connection

        if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
          //here the network changed to mobileData so I can send a heartbeat to GCM to keep connection
    }

我的解决方案好吗?


不必要的,GCM会自主完成这个任务。 - Tim

-4
在GCMIntentService类中,当您从服务器接收到消息时,onMessage方法将被调用,因此此时您可以执行以下操作...
PowerManager pm = (PowerManager) getApplicationContext()
                .getSystemService(Context.POWER_SERVICE);
        WakeLock wakeLock = pm.newWakeLock(
                        (PowerManager.SCREEN_BRIGHT_WAKE_LOCK
                                | PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP),"TAG");
        wakeLock.acquire();

你需要在你的清单文件中添加

<uses-permission android:name="android.permission.WAKE_LOCK" /> 权限。

这样就可以解决问题了...


1
谢谢您的回答,不过现在存活状态不是问题所在。我认为问题与长连接丢失有关,并且没有及时重新创建连接。 - Seidr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接