Android - Paho Mqtt client not receiving messages after network connection change (mobile data is disabled and re-enabled)

I am using Mosquitto Mqtt along with paho API to receive push messages on android device. But as soon as the network connection changes, it stops receiving messages. Following are the steps to reproduce the problem with a simple test case:

1) Create a simple activity.

2) On Activity StartUp connects to the mosquitto test server (test.mosquitto.org:1883) via the paho API.

3) Subscribe to some topics.

4) Post a post to the topic.

Result: The Mqtt client receives all messages posted to the topic. Now

5) Disconnect the internet connection on the mobile device (mobile data)

6) Post a message to the topic.

7) Connect the Internet.

Result: The client does not receive messages posted after the Internet connection was disconnected.

Since KeepAliveInterval is kept high (30 minutes), it should receive all messages after reconnecting to the Internet.

The same use case (same code) works for a simple java project (non-android) where I turn off the internet on my laptop to run the use case.

Any idea why it doesn't work on android device ??? Did I miss something?

Note:

1) Using mqtt-client-0.4.1

2) Android API Level 11

3) Without putting the device into sleep mode during the test.

4) Does not receive a call to the connectionLost callback and all 4 mqtt callback threads are executed throughout the test case. If the connection to the mosquitto server is intact.

+3


source to share


6 answers


The Java client library depends to some extent on the underlying networking API. When publish is called, it will write the MQTT packet to the socket. If this entry fails, then the connection will be ignored, if this entry works, then the client library will continue. The difference in behavior you see is that network libraries behave differently under these circumstances.

The MQTT keepalive interval is intended to help with this. Under certain circumstances, a TCP connection may appear to be alive if it is not. This is especially possible on mobile or satellite connected devices - you cannot expect network APIs to work the same under all circumstances. Keepalive sends a ping packet to the server and waits for a response - if this response is not received, the session is considered closed.



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

+3


source


You can connect an MqttCallback listener to MqttAsyncclient. It has a lost callback method that is called when a lost connection event has occurred or paho is disconnected.



+2


source


To fix this problem, I had to make an explicit request to the broker when connected to the internet (along with a timer to wait for a ping response). If the ping fails or the timer goes off, I forcefully terminate the existing connection (disconnectForcibly) and then explicitly call the connectionLost method. (Then connect again only to the connectionLost method).

+1


source


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

    }
}

      

+1


source


I fixed the recconect error 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 call example

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

      

+1


source


I already had this problem and fixed it by checking the MqttAndroidClient connection and using it .isConnected()

at regular intervals.

0


source







All Articles