如何避免Android GCM消息延迟/更改心跳

34
我已经阅读了许多关于GCM问题的帖子,这些问题影响了一些人。在某些设备/路由器/运营商上,通知会受到延迟的影响。我在我的路由器上遇到了这个问题:当我使用WIFI时,消息会有很大的延迟,但是当我禁用WIFI并连接到移动网络时,消息会立即出现。
似乎可以从Android应用程序中执行解决方法。例如增加保持GCM连接活动的心跳或其他方式。
有人能告诉我们如何避免延迟吗?我们如何从Android应用程序中与GCM保持连接?一个代码示例(以及在哪里以及如何使用它)将非常有帮助。 这篇文章解释了这个问题。它说“我们可以通过向GCM服务器发送每两分钟的ping来保持连接(这是免费的)”。我们该怎么做?
非常感谢。
以下应用程序似乎可以解决问题。令人惊讶的是,第一个应用程序不需要任何权限,第二个应用程序需要互联网连接,第三个应用程序仅适用于已root的手机。

https://play.google.com/store/apps/details?id=at.pansy.droid.gcmWifiFix

https://play.google.com/store/apps/details?id=com.elotro.pushheartbeat

https://play.google.com/store/apps/details?id=com.andqlimax.pushfixer


这很可能是由于GCM/FCM中不切实际的心跳间隔引起的。请考虑使用https://pushy.me,这是一个替代推送通知网关,在Android上极大地提高了通知速度和可靠性(完全透明 - 我创立了Pushy)。 - Elad Nava
5个回答

37

我希望将这个作为第一个答案的评论,但是我没有足够的声誉。

我遇到了类似的问题,解决方法是在调用Web服务之前执行以下代码。

context.sendBroadcast(new Intent("com.google.android.intent.action.GTALK_HEARTBEAT"));
context.sendBroadcast(new Intent("com.google.android.intent.action.MCS_HEARTBEAT"));

希望它能帮到你!


无论您在哪里都可以访问上下文对象(应用程序、活动、服务)。例如,如果您在活动中使用它,只需调用getContext()并使用上面的代码即可。 - Renan Grativol
能用 Cordova 实现这个吗? - Dr.Knowitall
在我的三星Note 4上无法工作,但在我检查的所有其他设备上(来自不同供应商的约10个设备)都可以完美运行。 - FunkSoulBrother
@RenanGrativol 你好,我也遇到了同样的问题,我能在我的单例类中使用上面的代码吗?并且使用以下引用?this.sendBroadcast(new Intent("com.google.android.intent.action.GTALK_HEARTBEAT")); this.sendBroadcast(new Intent("com.google.android.intent.action.MCS_HEARTBEAT")); - Chintan Khetiya
如果您可以访问这些方法,那么是的。无论您在何处访问上下文对象(应用程序、活动、服务等...)。 - Renan Grativol
如果您的应用程序被系统杀死,因此无法通过发送广播来修复其心跳间隔,但您需要用户接收实时消息,该怎么办? - Jacks Gong

10

不需要从GCM服务器发送心跳到手机,你可以强制Android自己更早地发送心跳。

我看了一下Push Notifications Fixer应用程序,我测试过了并且对我有效,似乎你只需要广播以下意图:

com.google.android.intent.action.MCS_HEARTBEAT
com.google.android.intent.action.GTALK_HEARTBEAT
我不确定为什么它会同时发送两个,你可以尝试只使用一个,看看是否有效。这是该应用程序的链接(我不是开发者): https://play.google.com/store/apps/details?id=com.andqlimax.pushfixer.noroot

2

如果您拥有root权限,则可以手动检查和更改GCM心跳时间。默认值为900000(15分钟)。我将其设置为240000(4分钟),以确保安全并避免5分钟路由器超时。

检查心跳时间(shell)

adb shell
su
sqlite3 /data/data/com.google.android.gsf/databases/gservices.db "SELECT * FROM main WHERE name ='gtalk_max_server_heartbeat_time';"
sqlite3 /data/data/com.google.android.gsf/databases/gservices.db "SELECT * FROM main WHERE name LIKE 'gtalk_%heartbeat_ping_interval_ms';"

改变心跳时间(Shell)

sqlite3 /data/data/com.google.android.gsf/databases/gservices.db "UPDATE main SET value = '240000' WHERE name = 'gtalk_max_server_heartbeat_time'
sqlite3 /data/data/com.google.android.gsf/databases/gservices.db "UPDATE main SET value = '240000' WHERE name LIKE 'gtalk_%heartbeat_ping_interval_ms'

更改心跳时间(Android)

Process proc = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(proc.getOutputStream());
os.writeBytes("sqlite3 /data/data/com.google.android.gsf/databases/gservices.db \"UPDATE main SET value = '240000' WHERE name = 'gtalk_max_server_heartbeat_time'\"\n");
os.writeBytes("sqlite3 /data/data/com.google.android.gsf/databases/gservices.db \"UPDATE main SET value = '240000' WHERE name LIKE 'gtalk_%heartbeat_ping_interval_ms'\"\n");
os.writeBytes("exit\n");
os.writeBytes("exit\n");
os.flush();
os.close();
proc.waitFor();

2

这对我有用,似乎也适用于 FCM,我感觉其他答案已经过时了? - JMK

1
"我们可以通过每两分钟从免费的GCM服务器发送一次ping来保持连接。如何实现?"
您可以通过从您的服务器向您希望执行此ping的所有设备发送GCM消息来实现这一点。您可以在该消息中放置一些特殊的有效负载,该有效负载将由设备上的应用程序静默处理(并不显示任何通知)。但是,这样的ping会缩短使用此应用程序的设备的电池寿命。

谢谢Eran。我们不能这样做,因为我们将不得不发送太多的通知——我们有数百万注册设备。我们能否从设备(从Android应用程序)进行“ping”操作? - Ferran Maylinch
1
@FerranMaylinch 可能是可能的。我从未尝试过从设备向自身发送通知,但没有理由不起作用(毕竟设备有自己的注册ID,只需向GCM服务器发布HTTP请求即可)。唯一的问题是它只在应用程序运行时才有效。 - Eran

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