40

在处理一个与 GCM 集成的小项目时,我偶然发现了一个奇怪的问题。

有时当我开始查看日志以查看是否收到消息时,消息似乎没有通过,直到我更改了网络状态(IE 最初在 WiFi 上,如果我关闭 WiFi 并移动到移动数据,则消息到达美好的)。在我更改网络状态后,消息开始完全正常地到达,一旦我将网络状态更改回之前的状态(在本例中为 WiFi),消息将继续被接收。

该项目本身包括启动时启动的能力(启动时启动 GCMBaseIntentService),它再次运行良好,我确信应用程序/服务正在运行,因为我在发生此问题时手动启动了应用程序(其中还检查服务是否正在运行,如果不是,则运行它并检查它是否已注册)。

有没有其他人遇到过这个问题,或者有任何关于我如何解决这个问题的指示?在未收到消息的时间和收到消息的时间(更改网络状态之后)之间,我在日志中没有看到任何帮助。我浏览了 GCM 文档,看不到由于超时(在设备本身上)或任何可能影响此的配置选项而未收到消息的任何提及。

感谢任何帮助 - 如果需要,我可以提供源代码,尽管它几乎不偏离 android-sdk 中提供的演示应用程序。

4

5 回答 5

39

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

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

有时,网络状态可能会发生变化,这个套接字会被破坏(因为设备的 IP 地址发生了变化,例如从 3g 变为 wifi)。如果消息在套接字重新建立之前进来,那么设备将不会立即得到消息。

仅当电话注意到套接字损坏时才会重新连接,这仅在它尝试发送心跳消息时才会发生。

同样,这只是我对它如何工作以及为什么会发生的基本理解,我可能是错的。

于 2013-02-13T18:49:28.693 回答
21

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

GCM 保持长期连接 - 如果它知道连接已断开,则重新连接。路由器/AP/NAT 应该发送一个 FIN 或 RST 来终止 TCP 连接 - 所以 GCM 和服务器将知道连接已死。

然而,一些路由器和移动运营商不这样做,然后 GCM 需要依靠心跳,在 Wifi 上大约 15 分钟,在移动设备上更多。电池寿命/网络使用和心跳频率之间存在权衡。

于 2013-12-16T20:14:43.157 回答
1

根据您在上述答案中的评论以及我对 GCM 推送通知的经验,没有任何理由表明如果网络(互联网连接)可用,您不应该收到推送通知,我总是在运行推送通知应用程序之前检查互联网连接可用性像这样尝试检查如果这是真的你应该收到推送通知

    private boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager 
          = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null;
}
于 2013-02-12T16:44:23.987 回答
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
    }

我的解决方案好吗?

于 2014-11-07T17:37:30.510 回答
-4

在 GCMIntentSevice 类中,当您收到来自服务器的消息时,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" /> 您清单文件中的权限..

这应该可以解决问题...

于 2013-02-09T08:43:56.017 回答