12

我们刚刚开始为 Android 构建自己的推送通知系统(由于客户的要求),并找到了 Eclipse Paho ( http://www.eclipse.org/paho/ )。不用说,这个项目真的很令人兴奋。

Android 的问题是,如果 CPU 处于睡眠状态,MQTT 客户端可能无法在其设置的时间间隔内发送 ping。解决方法是使用 AlarmManager 将其唤醒并完成工作。Android 文档说:

只要警报接收器的 onReceive() 方法正在执行,警报管理器就会持有 CPU 唤醒锁。这保证了在您完成广播处理之前手机不会休眠。一旦 onReceive() 返回,警报管理器就会释放这个唤醒锁。这意味着在某些情况下,一旦您的 onReceive() 方法完成,手机就会进入睡眠状态。

http://developer.android.com/reference/android/app/AlarmManager.html

我需要确保当 CPU 具有 PARTIAL_WAKE_LOCK 时我可以在 onReceive() 方法中发送 ping 命令,因此我正在寻找一种手动向服务器发送 ping 的方法,但似乎客户端没有公开任何此类方法。我错过了什么吗?或者,除了发布我自己的“ping 消息”之外,这里的解决方法是什么?我想避免这种情况,因为:

  1. 更大的开销
  2. 我们将确保 Android 客户端仅为订阅者,可能使用 Mosquitto 的 ACL。他们将不被允许发布消息。
4

4 回答 4

15

我一直在使用 Android 上的 MQTT 进行一些工作,并且遇到了完全相同的问题。

正如 Dale 所说,旧版本的 MQTT 客户端曾经有一个显式的 ping() 方法,但不幸的是,它现在被隐藏了。

最简单的方法,也是我使用的方法,是向特定主题显式发布一个 1 字节的消息,作为 keepalive。我认为这不会增加您的应用程序的开销,虽然我不熟悉 Mosquitto 的 ACL,但我认为您可以让每个客户端都使用相同的“keepalive”主题,并且只为所有客户端提供写访问权限。只要没有人可以阅读该主题,这不应该影响安全性。

另一种方法是让服务器以 QoS 1 或 2 向客户端发送“keepalive”消息(通过单个主题向所有人发布/订阅以提高效率),因为由于 QoS 流,这将涉及客户端在后台向服务器发送消息;这将作为保活。这样做的好处是让您的客户仅作为订阅者;但是它与“clean session = false”不兼容(因为您将有大量消息排队等待交付给离线一段时间的客户端 - 不必要地影响重新连接时的性能)。

不幸的是,这是我目前能想到的仅有的两种解决方法。


另外,顺便说一下,我在 Android 上使用 MqttDefaultFilePersistence 时遇到了许多问题,因此您可能需要注意这一点。特别是与重新实例化客户端时的文件锁定和问题有关。为了解决这个问题,我创建了一个构建在 SQLite 数据库之上的 MqttClientPersistence 实现,它更加健壮;你可能想做同样的事情。

于 2012-04-09T12:43:26.333 回答
10

大约一年前,我在为 Android 编写 MQTT 应用程序时遇到了这个问题。我已经在http://dalelane.co.uk/blog/?p=1599上详细地写过它,但简而言之,是的 - 我看到了同样的问题,你描述了当 MQTT 客户端时 CPU 是否处于睡眠状态应该发送它的 ping,然后 ping 永远不会被发送。

不同之处在于我使用了与您不同的 MQTT 客户端库(这是在 Paho 时代之前),而我使用的客户端库确实有一个我可以调用的 ping() 方法。(我的实现的完整来源在那个链接,它确实解决了这个问题)。

您不能扩展 Paho 客户端库的实现以包含 PING 命令吗?我认为这应该是一个相当小的修改。

于 2012-04-08T22:59:07.687 回答
2

有一种方法可以随时修改 paho 代码并进行 ping。如果我们使用发布主题来保持活动,我们必须向服务器发送至少 7 或 8 个字节。是的,8 个字节仍然不是大数据。但是MQTT的心跳只有2bytes。我们已经失去了 MQTT 的最大优势。

深入研究 paho 代码,我对其进行了修改并在 MQTTClient 中编写了一个名为 nnnn() 的公共方法。此方法可以将 MqttPingReq 发送到服务器。可以在这里找到实现... https://github.com/chinesejie/paho-for-android

于 2013-07-20T08:50:43.960 回答
2

我的解决方案:

(1) 修改:ClientComms comms;from protectedto public(in package 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();

希望这对你有帮助。

于 2013-11-16T10:22:58.733 回答