I am implementing the Dale Lane MQTT Android Service Example found here http://dalelane.co.uk/blog/?p=1599, the example is working pretty well for my specific purpose, but I am dealing with 1 issue that I cannot seem to resolve.
After testing rather extensively, I have found that the service does not reconnect after the phone has completely lost connection. For example, if the phone is placed in Airplane Mode and then taken out of Airplane mode, the service will continue to try to connectToBroker() but will never connect.
The application connects properly if I were to press the "connect" button on the main screen which starts the service with given host/topic parameters.
How can I resolve this issue? The application has no issue re-connecting from Wifi-Network, vice-versa.
02-16 12:19:39.348: E/mqtt(23628): ping failed - MQTT exception
02-16 12:19:39.348: E/mqtt(23628): com.ibm.mqtt.MqttNotConnectedException
02-16 12:19:39.348: E/mqtt(23628): at com.ibm.mqtt.Mqtt.writePacket(Unknown Source)
02-16 12:19:39.348: E/mqtt(23628): at com.ibm.mqtt.Mqtt.pingOut(Unknown Source)
02-16 12:19:39.348: E/mqtt(23628): at com.ibm.mqtt.MqttClient.ping(Unknown Source)
02-16 12:19:39.348: E/mqtt(23628): at com.limosys.limosystestmqtt.MQTTService$PingSender.onReceive(MQTTService.java:919)
02-16 12:19:39.348: E/mqtt(23628): at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:778)
02-16 12:19:39.348: E/mqtt(23628): at android.os.Handler.handleCallback(Handler.java:733)
02-16 12:19:39.348: E/mqtt(23628): at android.os.Handler.dispatchMessage(Handler.java:95)
02-16 12:19:39.348: E/mqtt(23628): at android.os.Looper.loop(Looper.java:136)
02-16 12:19:39.348: E/mqtt(23628): at android.app.ActivityThread.main(ActivityThread.java:5105)
02-16 12:19:39.348: E/mqtt(23628): at java.lang.reflect.Method.invokeNative(Native Method)
02-16 12:19:39.348: E/mqtt(23628): at java.lang.reflect.Method.invoke(Method.java:515)
02-16 12:19:39.348: E/mqtt(23628): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
02-16 12:19:39.348: E/mqtt(23628): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
02-16 12:19:39.348: E/mqtt(23628): at dalvik.system.NativeStart.main(Native Method)
Here is the stacktrace of the exception that is thrown if the ping fails, the application continues to try and connect every 10 seconds based on a given keepAliveSecond value.
CONNECTIONLOST
/*
* callback - method called when we no longer have a connection to the message broker server
*/
public void connectionLost() throws Exception {
// 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) getSystemService(POWER_SERVICE);
WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
wl.acquire();
//
// have we lost our data connection?
//
if (isOnline() == false) {
Log.e("CONNECTION LOST", "LOST CONNECTION");
connectionStatus = MQTTConnectionStatus.NOTCONNECTED_WAITINGFORINTERNET;
// inform the app that we are not connected any more
broadcastServiceStatus("Connection lost - no network connection");
//
// inform the user (for times when the Activity UI isn't running)
// that we are no longer able to receive messages
notifyUser("Connection lost - no network connection", "MQTT",
"Connection lost - no network connection");
//
// wait until the phone has a network connection again, when we
// the network connection receiver will fire, and attempt another
// connection to the broker
} else {
//
// we are still online
// the most likely reason for this connectionLost is that we've
// switched from wifi to cell, or vice versa
// so we try to reconnect immediately
//
connectionStatus = MQTTConnectionStatus.NOTCONNECTED_UNKNOWNREASON;
// inform the app that we are not connected any more, and are
// attempting to reconnect
broadcastServiceStatus("Connection lost - reconnecting...");
// try to reconnect
if (connectToBroker()) {
subscribeToTopic(topicName);
}
}
// we're finished - if the phone is switched off, it's okay for the CPU
// to sleep now
wl.release();
}
CONNECT TO BROKER This code is called several times after a service is available (ie, exiting airplane mode and connecting to 4G/Wifi)
/*
* (Re-)connect to the message broker
*/
private boolean connectToBroker() {
try {
// try to connect
Log.e("CONNECTTOBROKER", "TRYING TO CONNECT");
Log.e("SERVICE HOST - CONNECT TO BROKER", "" + brokerHostName);
Log.e("SERVICE TOPIC - CONNECT TO BROKER", "" + topicName);
mqttClient.connect(generateClientId(), cleanStart, keepAliveSeconds);
//
// inform the app that the app has successfully connected
broadcastServiceStatus("Connected");
// we are connected
connectionStatus = MQTTConnectionStatus.CONNECTED;
// we need to wake up the phone's CPU frequently enough so that the
// keep alive messages can be sent
// we schedule the first one of these now
scheduleNextPing();
return true;
} catch (MqttException e) {
// something went wrong!
connectionStatus = MQTTConnectionStatus.NOTCONNECTED_UNKNOWNREASON;
//
// inform the app that we failed to connect so that it can update
// the UI accordingly
broadcastServiceStatus("Unable to connect");
//
// inform the user (for times when the Activity UI isn't running)
// that we failed to connect
notifyUser("Unable to connect", "MQTT", "Unable to connect - will retry later");
// if something has failed, we wait for one keep-alive period before
// trying again
// in a real implementation, you would probably want to keep count
// of how many times you attempt this, and stop trying after a
// certain number, or length of time - rather than keep trying
// forever.
// a failure is often an intermittent network issue, however, so
// some limited retry is a good idea
scheduleNextPing();
return false;
}
}
Download the latest android service jar(1.02) and java client(1.02) from paho. Latest Android service jars have the fix for this issue. It takes care for reconnecting once the network connection is back.
add this inside onResume() of the Activity
and this inside onDestroy() of the same Activity
this worked for me.