Android - Paho Mqtt client does not receive messages once network connectivity changes (mobile data disabled and enabled again)

久未见 提交于 2019-12-05 16:41:15

The Java client library is at the mercy of the underlying networking API to a certain extent. When publish is called, it will write an MQTT packet to the socket. If that write fails, then connection lost will be called, if that write works then the client library will carry on. The difference in behaviour you are seeing is because the networking libraries are behaving differently in these circumstances.

The MQTT keepalive interval is meant to help with this. Under certain circumstances a TCP connection may appear to be live when it is not. This is especially possible on mobile or satellite connected devices - you can't expect the networking APIs to work exactly the same in all circumstances. Keepalive sends a ping packet to the server and expects a response - if that response is not received, the session is assumed to be closed.

If you set the keepalive interval to say 10 seconds, then the connection should be recognised as broken within 15 to 20 seconds.

You can attach MqttCallback listener to MqttAsyncclient . It has callback method connection lost which will get called when connection lost event occured or paho disconnects.

To fix the issue, I had to make an explicit ping to the broker whenever internet connection is back on (along with a timer to wait for ping response). If ping fails or timer goes out, I forcefully terminate the existing connection (disconnectForcibly) and then explicitly call connectionLost method. (Then reconnect from connectionLost method only).

In Your Service :-

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

Create the Following Class:-

/*
* 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();
}

Now Call the Following Method Somewhere Appropriate in OnResume() or onCreate to Register the 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));

    }
}

I fixed recconect bug as follows (using rxJava2, but not required):

    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();
    }

and an example call

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();
            }
        }
    }
};

I already had this problem and fix it with check MqttAndroidClient connection and using .isConnected() in time intervals.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!