Android - Paho Mqtt客户端在网络连接更改后不会收到消息(移动数据已禁用并再次启用)

时间:2014-08-07 07:32:11

标签: java android mqtt mosquitto paho

我正在使用Mosquitto Mqtt和paho API在Android设备上接收推送消息。但是一旦网络连接发生变化,就会停止接收消息。以下是使用简单测试用例重现问题的步骤:

1)创建一个简单的活动。

2)On Activity StartUp通过paho API连接到mosquitto测试服务器(test.mosquitto.org:1883)。

3)订阅一些主题。

4)向主题发布一些消息。

结果: Mqtt客户端会收到发布到该主题的所有消息。现在

5)禁用手机上的互联网连接(移动数据)

6)向主题发布一些消息。

7)重新连接互联网。

结果:客户端在禁用互联网连接后,不会收到任何已发布的消息。

由于 KeepAliveInterval 已保持高值(30分钟),因此重新连接到互联网后应收到所有消息。

相同的用例(相同的代码)适用于简单的java项目(非Android),我在笔记本电脑上禁用互联网来运行用例。

知道为什么它不能在Android设备上运行???我错过了什么吗?

注意:

1)使用mqtt-client-0.4.1

2)Android目标API等级11

3)测试期间不要将设备置于睡眠模式。

4)在connectionLost回调中没有得到任何调用,mqtt回调的所有4个线程都在整个测试用例中运行,即连接mosquitto服务器是完整的。

6 个答案:

答案 0 :(得分:3)

Java客户端库在某种程度上受底层网络API的支配。调用publish时,它会将MQTT数据包写入套接字。如果该写入失败,则将调用连接丢失,如果该写入有效,则客户端库将继续。您看到的行为差异是因为网络库在这些情况下表现不同。

MQTT keepalive间隔旨在帮助解决这个问题。在某些情况下,TCP连接可能看起来是实时的。这在移动或卫星连接设备上尤其可行 - 您无法期望网络API在所有情况下都能完全相同。 Keepalive将ping数据包发送到服务器并期望响应 - 如果未收到响应,则假定会话已关闭。

如果将keepalive间隔设置为10秒,则应在15到20秒内将连接识别为已断开。

答案 1 :(得分:2)

您可以将MqttCallback侦听器附加到MqttAsyncclient。它有回调方法连接丢失,当连接丢失事件发生或paho断开时将被调用。

答案 2 :(得分:1)

要解决这个问题,每当互联网连接恢复时,我都必须向代理发出明确的ping(以及等待ping响应的计时器)。如果ping失败或计时器熄灭,我强行终止现有连接(disconnectForcibly),然后显式调用connectionLost方法。 (然后仅从connectionLost方法重新连接)。

答案 3 :(得分:1)

在您的服务中: -

 //Receiver that notifies the Service when the phone gets data connection
  private NetworkConnectionIntentReceiver netConnReceiver;

创建以下类: -

/*
* Called in response to a change in network connection - after losing a
*  connection to the server, this allows us to wait until we have a usable
*  data connection again
*/
class NetworkConnectionIntentReceiver extends BroadcastReceiver
{
  private static  String TAG ="NetworkConnectionIntentReceiver";
  @Override
  public void onReceive(Context ctx, Intent intent)
  {
    // we protect against the phone switching off while we're doing this
    //  by requesting a wake lock - we request the minimum possible wake
    //  lock - just enough to keep the CPU running until we've finished

    PowerManager pm = (PowerManager) ctx.getSystemService(ctx.POWER_SERVICE);
    PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
    wl.acquire();

    Connection c = Connections.getInstance(ctx).getConnection(clientHandle);
    final ActionListener callback = new ActionListener(ctx,
                ActionListener.Action.CONNECT, clientHandle,null);
    c.getClient().setCallback(new MqttCallbackHandler(ctx, clientHandle,messenger_where_incoming_messages_tobe_sent));
    c.getClient().connect(c.getConnectionOptions(), null, callback);

    /*    The Above Reconnect Logic can be put up in a Reconnect() function.
     *    OR WRITE Any Other LOGIC TO RECONNECT TO MQTT
     */       

    // we're finished - if the phone is switched off, it's okay for the CPU
    //  to sleep now
    wl.release();
}

现在在OnResume()或onCreate中适当的地方调用以下方法来注册BroadcastReceiver。

synchronized void handleNetworkChange()
{

    // changes to the phone's network - such as bouncing between WiFi
    //  and mobile data networks - can break the MQTT connection
    // the MQTT connectionLost can be a bit slow to notice, so we use
    //  Android's inbuilt notification system to be informed of
    //  network changes - so we can reconnect immediately, without
    //  haing to wait for the MQTT timeout
    if (netConnReceiver == null)
    {
        netConnReceiver = new NetworkConnectionIntentReceiver();
        registerReceiver(netConnReceiver,
                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));

    }
}

答案 4 :(得分:1)

我修复了recconect bug如下(使用rxJava2,但不是必需的):

    public void reconnect() {
        Completable.create(emitter -> {
            while (!mqttClient.isConnected()) {
                mqttClient.connect(options, null, new IMqttActionListener() {
                    @Override
                    public void onSuccess(IMqttToken asyncActionToken) {
                        emitter.onComplete();
                    }

                    @Override
                    public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                        LogHelper.d(TAG,"try to connect failed");
                    }
                });

                Thread.sleep(2000);
            }
            emitter.onComplete();
        })
        .subscribeOn(Schedulers.io())
        .subscribe();
    }

和示例调用

private BroadcastReceiver changeNetworkStateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Objects.equals(intent.getAction(), NetworkStateReceiver.EVENT_CHANGE_NETWORK_STATE)) {
            if(Utils.isOnline(context)) {
                mqttClient.reconnect();
            }
        }
    }
};

答案 5 :(得分:0)

我已经遇到此问题,并通过检查MqttAndroidClient连接并在时间间隔内使用.isConnected()来解决。