我们刚刚开始为Android构建我们自己的推送通知系统(由于客户的要求),并找到了Eclipse Paho(http://www.eclipse.org/paho/)。不用说,这个项目非常令人兴奋。
Android的问题是,如果CPU处于睡眠状态,MQTT客户端可能无法以设置的间隔发送ping。解决方法是使用AlarmManager将其唤醒并完成工作。 Android文档说:
只要报警,报警管理器就会保持CPU唤醒锁定 接收者的onReceive()方法正在执行。这保证了 在完成广播处理后,手机才会睡眠。 一旦onReceive()返回,Alarm Manager就会释放此唤醒锁定。 这意味着手机在某些情况下会尽快睡觉 onReceive()方法完成。
http://developer.android.com/reference/android/app/AlarmManager.html
我需要确保我可以在该onReceive()方法中发送ping命令,而CPU有PARTIAL_WAKE_LOCK,所以我正在搜索手动发送ping到服务器的方法,但似乎客户端没有暴露任何这样的方法。我错过了什么吗?或者,除了发布我自己的“ping消息”之外,这里的解决方法是什么?我想避免这种情况,因为:
答案 0 :(得分:15)
我一直在Android上使用MQTT做一些工作,我遇到了完全相同的问题。
正如Dale所说,旧版本的MQTT客户端曾经有一个明确的ping()方法,但不幸的是现在已经隐藏了它。
最简单的方法,也就是我使用的方法,是明确地向特定主题发布1字节消息,以充当keepalive。我认为这不应该增加应用程序的开销,虽然我不熟悉Mosquitto的ACL,但我认为你可以让每个客户端使用相同的“keepalive”主题并只提供对所有人的写访问权限。只要没有人可以从主题中读取,这不应该影响安全性。
另一种方法是让服务器在QoS 1或2发送客户端的“keepalive”消息(通过单个主题发布/发布以提高效率),因为QoS流量,将涉及客户端将消息发送回封底的服务器;这将作为keepalive。这样做的好处是只保留您的客户为订户;但它与'clean session = false'不兼容(因为你会将大量的消息排队等待交付给离线一段时间的客户端 - 不必要地影响重新连接的性能)。
不幸的是,这些是我目前唯一能想到的两种解决方法。
另外,作为一个简短的说法,我在Android上使用MqttDefaultFilePersistence时遇到了很多问题,所以你可能想知道这一点。特别是在重新实例化客户端时与文件锁定和问题有关。为了解决这个问题,我创建了一个构建在SQLite数据库之上的MqttClientPersistence实现,这个实现更加强大;你可能想要这样做。
答案 1 :(得分:10)
我在大约一年前为Android编写MQTT应用程序时遇到过这个问题。我已经在http://dalelane.co.uk/blog/?p=1599处写了一些篇幅但是简而言之,是的 - 我看到了同样的问题,你描述了当MQTT客户端发送它的ping时CPU是否处于睡眠状态时,ping永远不会被发送。
不同之处在于我正在使用不同的MQTT客户端库(这是在Paho时代之前),而我使用的客户端库确实有一个我可以调用的ping()方法。 (我的实现的完整源代码在那个链接上,它确实解决了这个问题)。
您是否可以扩展Paho客户端库的实现以包含PING命令?我认为它应该是一个相当小的修改。
答案 2 :(得分:2)
有一种方法可以随时修改paho代码并进行ping操作。如果我们使用发布主题来保持活跃,我们必须向服务器发送至少7或8个字节。是的,8个字节仍然不是大数据。但MQTT的心跳只有2字节。我们失去了MQTT的最大优势。
深入研究paho代码,我修改它并在MQTTClient中编写一个名为nnnn()的公共方法。此方法可以将MqttPingReq发送到服务器。实现可以在这里找到...... https://github.com/chinesejie/paho-for-android
答案 3 :(得分:2)
我的解决方案:
(1)修改:ClientComms comms;
从protected
到public
(在包org.eclipse.paho.client.mqttv3
中)
public class MqttAsyncClient implements IMqttAsyncClient { // DestinationProvider {
//...
public ClientComms comms; // Add by Ben for pingreq*
//...
}
(2)定义新类:(派生自MqttClient
)
public class MqttClient2 extends MqttClient {
public MqttClient2(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
super(serverURI, clientId, persistence);
}
public void pingreq() throws MqttException {
MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
MqttPingReq pingMsg = new MqttPingReq();
aClient.comms.sendNoWait(pingMsg, token);
}
}
(3)任何地方,你都可以:
MqttClient2 mClient = new MqttClient2(url, mDeviceId, mDataStore);
mClient.pingreq();
希望这对你有所帮助。