Java Android-一旦网络连接发生变化,Paho Mqtt客户端将不接收消息(移动数据被禁用并再次启用)

Java Android-一旦网络连接发生变化,Paho Mqtt客户端将不接收消息(移动数据被禁用并再次启用),java,android,mqtt,mosquitto,paho,Java,Android,Mqtt,Mosquitto,Paho,我正在使用Mosquito Mqtt和paho API在android设备上接收推送消息。但一旦网络连接发生变化,它就会停止接收消息。以下是使用简单测试用例重现问题的步骤: 1) 创建一个简单的活动 2) 在活动启动时,通过paho API连接到mosquitto测试服务器(test.mosquitto.org:1883) 3) 订阅一些主题 4) 向主题发布一些消息 结果:Mqtt客户端接收发布到主题的所有消息。现在 5) 禁用移动设备上的internet连接(移动数据) 6) 向主题发布一些

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

1) 创建一个简单的活动

2) 在活动启动时,通过paho API连接到mosquitto测试服务器(test.mosquitto.org:1883)

3) 订阅一些主题

4) 向主题发布一些消息

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

5) 禁用移动设备上的internet连接(移动数据)

6) 向主题发布一些消息

7) 重新连接互联网

结果:禁用internet连接后,客户端未收到任何发布的消息

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

相同的用例(相同的代码)适用于简单java项目(非android),我在笔记本电脑上禁用internet以运行该用例。

知道为什么它不能在安卓设备上工作吗???我错过什么了吗

注意:

1) 使用mqtt-client-0.4.1

2) Android目标API 11级

3) 测试期间未将设备置于睡眠模式


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

Java客户端库在一定程度上受底层网络API的支配。调用publish时,它将向套接字写入MQTT数据包。如果写入失败,那么将调用连接丢失,如果写入成功,那么客户端库将继续。您看到的行为差异是因为网络库在这些情况下的行为不同

MQTT keepalive间隔旨在帮助实现这一点。在某些情况下,TCP连接可能看起来是活动的,而实际上不是。这在移动或卫星连接的设备上尤其可能——您不能期望网络API在所有情况下都完全相同。Keepalive向服务器发送一个ping数据包,并期望得到响应——如果没有收到该响应,则假定会话已关闭


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

您可以将MqttCallback listener连接到MqttAsyncclient。它有一个回调方法connection lost,当发生连接丢失事件或paho断开连接时将调用该方法。

为了解决这个问题,每当internet连接重新打开时,我必须对代理进行显式ping(以及等待ping响应的计时器)。如果ping失败或计时器熄灭,我将强制终止现有连接(disconnectforcely),然后显式调用connectionLost方法。(然后仅从connectionLost方法重新连接)。

在您的服务中:-

 //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));

    }
}

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

public void reconnect(){
可完成。创建(发射器->{
而(!mqttClient.isConnected()){
mqttClient.connect(选项,null,新的IMqttActionListener(){
@凌驾
成功时公共无效(IMqttToken asyncActionToken){
emitter.onComplete();
}
@凌驾
public void onFailure(IMqttToken asyncActionToken,可丢弃异常){
LogHelper.d(标记“尝试连接失败”);
}
});
《睡眠》(2000年);
}
emitter.onComplete();
})
.subscribeOn(Schedulers.io())
.subscribe();
}
还有一个例子

private BroadcastReceiver changeNetworkStateReceiver=new BroadcastReceiver(){
@凌驾
公共void onReceive(上下文、意图){
if(Objects.equals(intent.getAction(),NetworkStateReceiver.EVENT\u CHANGE\u NETWORK\u STATE)){
if(Utils.isOnline(上下文)){
mqttClient.reconnect();
}
}
}
};

我已经遇到了这个问题,并通过检查MqttAndroidClient connection和每隔一段时间使用
.isConnected()
来修复它。

谢谢Anubhav,但我知道连接丢失回调。看看我笔记中的第四点。为了解决这个问题,我必须想出一个解决办法。在下面找到我的答案。您正在发送qos1或qos 0消息,因为如果您尝试在断开连接状态下发送qos0数据包,当您进入连接状态时,它们将不会重试。您还应该使用自己的持久性来存储qos1消息,以便稍后重试,因为paho中的持久性有一个bug。如果您处于断开连接状态,它将不会将消息存储到Peristence。我正在使用qos2并在重新连接后获取所有消息,因此这不是问题:)@anubhavgupta您提到了paho持续存在的错误,您是否参考过它?。我遇到了您描述的问题(mesg未保持断开连接状态)@Linus paho lib中ClientComms类中有sendnowait函数。它基本上是在发送之前检查客户端是否已连接。持久化步骤在internalsend函数中紧跟其后。所以,如果客户端未连接,则不会调用internalsend,消息也不会持久化。10秒的keepalive不会导致android上的电池大量消耗吗?