Reputation: 13
I am now trying to use MQTT library and it's able that two other local hosts can communicate with one another (this sample code: https://github.com/bytehala/android-mqtt-quickstart)
But the only thing I have to resolve is that
While turned off, receiving messages is not available
Please, let me know how to operate in the background
my code mqttcallbackhandler.java
public class MqttCallbackHandler implements MqttCallback {
/** {@link Context} for the application used to format and import external strings**/
private Context context;
/** Client handle to reference the connection that this handler is attached to**/
private String clientHandle;
MainActivity main;
/**
* Creates an <code>MqttCallbackHandler</code> object
* @param context The application's context
* @param clientHandle The handle to a {@link Connection} object
*/
public MqttCallbackHandler(Context context, String clientHandle)
{
this.context = context;
this.clientHandle = clientHandle;
}
/**
* @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang.Throwable)
*/
@Override
public void connectionLost(Throwable cause) {
// cause.printStackTrace();
if (cause != null) {
Connection c = Connections.getInstance(context).getConnection(clientHandle);
c.addAction("Connection Lost");
c.changeConnectionStatus(ConnectionStatus.DISCONNECTED);
//format string to use a notification text
Object[] args = new Object[2];
args[0] = c.getId();
args[1] = c.getHostName();
String message = context.getString(R.string.connection_lost, args);
//build intent
Intent intent = new Intent();
intent.setClassName(context, "org.eclipse.paho.android.service.sample.MainActivity");
intent.putExtra("handle", clientHandle);
//notify the user
Notify.notifcation(context, message, intent, R.string.notifyTitle_connectionLost);
}
}
/**
* @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage)
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
//Get connection object associated with this object
Connection c = Connections.getInstance(context).getConnection(clientHandle);
//create arguments to format message arrived notifcation string
String[] args = new String[2];
args[0] = new String(message.getPayload());
args[1] = topic+";qos:"+message.getQos()+";retained:"+message.isRetained();
//get the string from strings.xml and format
String messageString = context.getString(R.string.messageRecieved, (Object[]) args);
//create intent to start activity
Intent intent = new Intent();
intent.setClassName(context, "org.eclipse.paho.android.service.sample.ConnectionDetails");
intent.putExtra("handle", clientHandle);
//format string args
Object[] notifyArgs = new String[3];
notifyArgs[0] = c.getId();
notifyArgs[1] = new String(message.getPayload());
notifyArgs[2] = topic;
Log.d("won", "msg2=" + notifyArgs[1] + "");
MainActivity.MessageReceive(notifyArgs[1] + "");
//notify the user
// Notify.notifcation(context, context.getString(R.string.notification, notifyArgs), intent, R.string.notifyTitle);
//update client history
c.addAction(messageString);
}
/**
* @see org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho.client.mqttv3.IMqttDeliveryToken)
*/
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// Do nothing
}
}
Upvotes: 1
Views: 7514
Reputation: 424
From android Oreo, because of features like Doze, App Stanby, Battery Optimization, App Bucket and Battery Saver, normal service and Job Scheduler are not guaranteed to have network access and continuously running in background. To use MQTT, we need to use a foreground service together with a wakelock to always keep connection to server to check for messages even when the phone screen is off. https://developer.android.com/guide/components/services#Foreground
Upvotes: 2
Reputation: 403
well you could use a service for that that is executed when your application begins
In your manifest add this so that you declare your service
<service
android:enabled="true"
android:name=".Mqttservice"
/>
Mqttservice.java
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Mqttservice extends Service {
private String ip="brokerip",port="1883";
private final IBinder mBinder = new LocalBinder();
private Handler mHandler;
private class ToastRunnable implements Runnable {//to toast to your main activity for some time
String mText;
int mtime;
public ToastRunnable(String text, int time) {
mText = text;
mtime = time;
}
@Override
public void run() {
final Toast mytoast = Toast.makeText(getApplicationContext(), mText, Toast.LENGTH_LONG);
mytoast.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
mytoast.cancel();
}
}, mtime);
}
}
private static final String TAG = "mqttservice";
private static boolean hasWifi = false;
private static boolean hasMmobile = false;
private ConnectivityManager mConnMan;
private volatile IMqttAsyncClient mqttClient;
private String uniqueID;
class MQTTBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
IMqttToken token;
boolean hasConnectivity = false;
boolean hasChanged = false;
NetworkInfo infos[] = mConnMan.getAllNetworkInfo();
for (int i = 0; i < infos.length; i++) {
if (infos[i].getTypeName().equalsIgnoreCase("MOBILE")) {
if ((infos[i].isConnected() != hasMmobile)) {
hasChanged = true;
hasMmobile = infos[i].isConnected();
}
Log.d(TAG, infos[i].getTypeName() + " is " + infos[i].isConnected());
} else if (infos[i].getTypeName().equalsIgnoreCase("WIFI")) {
if ((infos[i].isConnected() != hasWifi)) {
hasChanged = true;
hasWifi = infos[i].isConnected();
}
Log.d(TAG, infos[i].getTypeName() + " is " + infos[i].isConnected());
}
}
hasConnectivity = hasMmobile || hasWifi;
Log.v(TAG, "hasConn: " + hasConnectivity + " hasChange: " + hasChanged + " - " + (mqttClient == null || !mqttClient.isConnected()));
if (hasConnectivity && hasChanged && (mqttClient == null || !mqttClient.isConnected())) {
doConnect();
}
}
}
public class LocalBinder extends Binder {
public Mqttservice getService() {
// Return this instance of LocalService so clients can call public methods
return Mqttservice.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void publish(String topic, MqttMessage message) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);// we create a 'shared" memory where we will share our preferences for the limits and the values that we get from onsensorchanged
try {
mqttClient.publish(topic, message);
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
mHandler = new Handler();//for toasts
IntentFilter intentf = new IntentFilter();
setClientID();
intentf.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(new MQTTBroadcastReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
Log.d(TAG, "onConfigurationChanged()");
android.os.Debug.waitForDebugger();
super.onConfigurationChanged(newConfig);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("Service", "onDestroy");
}
private void setClientID() {
uniqueID = android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
Log.d(TAG, "uniqueID=" + uniqueID);
}
private void doConnect() {
String broker = "tcp://" + ip + ":" + port;
Log.d(TAG, "mqtt_doConnect()");
IMqttToken token;
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true);
options.setMaxInflight(100);//handle more messages!!so as not to disconnect
options.setAutomaticReconnect(true);
options.setConnectionTimeout(1000);
try {
mqttClient = new MqttAsyncClient(broker, uniqueID, new MemoryPersistence());
token = mqttClient.connect(options);
token.waitForCompletion(3500);
mqttClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable throwable) {
try {
mqttClient.disconnectForcibly();
mqttClient.connect();
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void messageArrived(String topic, MqttMessage msg) throws Exception {
Log.i(TAG, "Message arrived from topic " + topic);
if (topic.equals("Sensors/message")) {
} else if (topic.equals("Sensors/" + uniqueID)) {
}
else{
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println("published");
}
});
mqttClient.subscribe("Sensors/" + uniqueID, 0);
mqttClient.subscribe("Sensors/message", 0);
} catch (MqttSecurityException e) {
e.printStackTrace();
} catch (MqttException e) {
switch (e.getReasonCode()) {
case MqttException.REASON_CODE_BROKER_UNAVAILABLE:
mHandler.post(new ToastRunnable("WE ARE OFFLINE BROKER_UNAVAILABLE!", 1500));
break;
case MqttException.REASON_CODE_CLIENT_TIMEOUT:
mHandler.post(new ToastRunnable("WE ARE OFFLINE CLIENT_TIMEOUT!", 1500));
break;
case MqttException.REASON_CODE_CONNECTION_LOST:
mHandler.post(new ToastRunnable("WE ARE OFFLINE CONNECTION_LOST!", 1500));
break;
case MqttException.REASON_CODE_SERVER_CONNECT_ERROR:
Log.v(TAG, "c" + e.getMessage());
e.printStackTrace();
break;
case MqttException.REASON_CODE_FAILED_AUTHENTICATION:
Intent i = new Intent("RAISEALLARM");
i.putExtra("ALLARM", e);
Log.e(TAG, "b" + e.getMessage());
break;
default:
Log.e(TAG, "a" + e.getMessage());
}
}
mHandler.post(new ToastRunnable("WE ARE ONLINE!", 500));
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand()");
return START_STICKY;
}
}
in your main in oncreate add this
Intent mymqttservice_intent = new Intent(this, Mqttservice.class);
startService(mymqttservice_intent);
Upvotes: 3