I have foreground service acting as MQTT client. I'm using MqttAsyncClient mqttClient for this purpose.
I'm using QoS=1 on subscribe to topic:
mqttClient.subscribe("sensors/s1/", 1);
But in case my phone gets offline for some period of time it miss current period messages. Whole code is below.
Im my another application I'm using MqttAndroidClient mqttAndroidClient and in this case QoS=1 brings all missed messages.
mqttAndroidClient.subscribe(topic, 1, null, new IMqttActionListener() {...})
Why subscription with MqttAsyncClient with QoS=1 not retrieves all messages?
Whole code :
public class MqttGndService extends Service {
private String ip="ssl:myserver",port="8887";
private final IBinder mBinder = new LocalBinder();
private Handler mHandler;
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();
}
Timber.tag(Utils.TIMBER_TAG).v( 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();
}
Timber.tag(Utils.TIMBER_TAG).v(infos[i].getTypeName() + " is " + infos[i].isConnected());
}
}
hasConnectivity = hasMmobile || hasWifi;
Timber.tag(Utils.TIMBER_TAG).v( "hasConn: " + hasConnectivity + " hasChange: " + hasChanged + " - " + (mqttClient == null || !mqttClient.isConnected()));
if (hasConnectivity && hasChanged && (mqttClient == null || !mqttClient.isConnected())) {
Timber.tag(Utils.TIMBER_TAG).v("Ready to connect");
doConnect();
Timber.tag(Utils.TIMBER_TAG).v("do connect done");
} else
{
Timber.tag(Utils.TIMBER_TAG).v("Connection not possible");
}
}
}
public class LocalBinder extends Binder {
public MqttGndService getService() {
// Return this instance of LocalService so clients can call public methods
return MqttGndService.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() {
Timber.tag(Utils.TIMBER_TAG).v("Creating MQTT service");
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) {
Timber.tag(Utils.TIMBER_TAG).v( "onConfigurationChanged()");
android.os.Debug.waitForDebugger();
super.onConfigurationChanged(newConfig);
}
#Override
public void onDestroy() {
super.onDestroy();
Timber.tag(Utils.TIMBER_TAG).v("Service onDestroy");
}
private void setClientID() {
uniqueID = android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
Timber.tag(Utils.TIMBER_TAG).v("uniqueID=" + uniqueID);
}
private void doConnect() {
String broker = ip + ":" + port;
Timber.tag(Utils.TIMBER_TAG).v("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);
options.setKeepAliveInterval(300);
options.setUserName("cc50e3e91bf4");
options.setPassword("b".toCharArray());
try {
options.setSocketFactory(SocketFactoryMQ.getSocketFactory(this,""));
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
Timber.tag(Utils.TIMBER_TAG).v("set socket factory done");
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 {
Timber.tag(Utils.TIMBER_TAG).v("Message arrived from topic " + topic+ " msg: " + msg );
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println("published");
}
});
Timber.tag(Utils.TIMBER_TAG).v("will subscribe");
mqttClient.subscribe("sensors/s1/", 1);
} catch (MqttSecurityException e) {
Timber.tag(Utils.TIMBER_TAG).v("general connect exception");
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:
Timber.tag(Utils.TIMBER_TAG).v( "c " + e.getMessage());
e.printStackTrace();
break;
case MqttException.REASON_CODE_FAILED_AUTHENTICATION:
Intent i = new Intent("RAISEALLARM");
i.putExtra("ALLARM", e);
Timber.tag(Utils.TIMBER_TAG).v("b " + e.getMessage());
break;
default:
Timber.tag(Utils.TIMBER_TAG).v( "a " + e.getMessage() +" "+ e.toString());
}
}
mHandler.post(new ToastRunnable("WE ARE ONLINE!", 500));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Timber.tag(Utils.TIMBER_TAG).v("onStartCommand");
String input = intent.getStringExtra(INTENT_ID);
Timber.tag(Utils.TIMBER_TAG).v("onStartCommand "+ input);
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag");
wakeLock.acquire();
return START_STICKY;
}
}
You are setting cleansession to true (options.setCleanSession(true)); from the docs for setCleanSession:
If set to true the client and server will not maintain state across restarts of the client, the server or the connection. This means
Message delivery to the specified QOS cannot be maintained if the client, server or connection are restarted
The server will treat a subscription as non-durable
I think that the mqtt specs state this more clearly:
If CleanSession is set to 1, the Client and Server MUST discard any previous Session and start a new one. This Session lasts as long as the Network Connection. State data associated with this Session MUST NOT be reused in any subsequent Session
So when your application looses the connection the session is discarded and new messages will not be queued up for delivery. In addition unless you resubscribe when the connection comes back up you will not receive any additional messages.
However be aware that if you set cleansession to false then any new messages received while your client is offline will be queued for delivery (subject to the configuration of the broker) and this might not be what you want to happen if the client could be offline for a long time.
Related
I am using mqtt android client to stream locations. This works fine until when the internet connection is lost. The mqtt client does not reconnect as expected. What should I do to make sure the paho mqtt reconnects to the broker.
I want to force the mqtt client to reconnect to the broker and continue to publish locations when a connection returns.
Below is my code.
public class MqttClientHelperService2 extends Service {
private MqttAndroidClient mqttAndroidClient;
BroadcastReceiver broadcastReceiver;
private final String SERVER_URL = "tcp://000.102.110.**:1883";
private final String CLIENT_ID = "client_id";
private final String MQTT_TOPIC = "livelocations/local";
LiveLocation liveLocation;
#Override
public void onCreate() {
super.onCreate();
Thread newThread = new Thread(){
public void run(){
init();
}
};
newThread.start();
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "my_channel_01";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"Channel human readable title",
NotificationManager.IMPORTANCE_LOW);
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).
createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("")
.setSmallIcon(R.mipmap.ic_launcher).build();
startForeground(1, notification);
}
}
private void init() {
mqttAndroidClient = new MqttAndroidClient(getApplicationContext(), SERVER_URL, CLIENT_ID);
mqttAndroidClient.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable cause) {
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
}
});
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
liveLocation= new LiveLocation(
intent.getIntExtra("driver_id", 0),
intent.getDoubleExtra("latitude", 0),
intent.getDoubleExtra("longitude", 0),
"");
connectMqtt();
return START_STICKY;
}
private MqttConnectOptions getMqttOptions(){
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
mqttConnectOptions.setAutomaticReconnect(true);
mqttConnectOptions.setCleanSession(false);
return mqttConnectOptions;
}
private void connectMqtt() {
try {
IMqttToken iMqttToken = mqttAndroidClient.connect(getMqttOptions());
iMqttToken.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.e("phanuel-log", "connecting ......." + Calendar.getInstance().getTime());
publishToServer(liveLocation);
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.e("phanuel-log-error", "connection error");
}
});
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void onDestroy() {
if(mqttAndroidClient != null){
try {
mqttAndroidClient.unregisterResources();
mqttAndroidClient.close();
mqttAndroidClient.disconnect();
} catch (Exception e){
e.printStackTrace();
}
}
unregisterReceiver(broadcastReceiver);
broadcastReceiver = null;
super.onDestroy();
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
assert connectivityManager != null;
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting();
}
void publishToServer(final LiveLocation location) {
try {
if (location.getDriverId() > 0){
mqttAndroidClient.connect(getMqttOptions(), null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
JSONObject locationJsonObject = new JSONObject();
try {
locationJsonObject.put("driverId", location.getDriverId());
locationJsonObject.put("driverLatitude", location.getLatitude());
locationJsonObject.put("driverLongitude", location.getLongitude());
locationJsonObject.put("driverTimeStamp", Calendar.getInstance().getTime());
} catch (JSONException e) {
e.printStackTrace();
}
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(locationJsonObject.toString().getBytes());
mqttMessage.setQos(0);
mqttMessage.setRetained(false);
try {
mqttAndroidClient.publish(MQTT_TOPIC, mqttMessage);
}catch (MqttException e){
e.printStackTrace();
}
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.e("mqtt-client", "Failed to connect");
Log.e("mqtt-cleint", exception.toString());
}
});
}
} catch (MqttException e){
e.printStackTrace();
}
}
}
There are multiple ways to handle your scenario.
If you are using mqtt only to publish from android side then you can
call init() function (connect to mqtt server) before publishing in
mqtt. After message published you can disconnect from mqtt server.
If you always need alive mqtt connection then whenever mqtt connection is broken
then connectionLost method is called. You can reconnect at this method.
You can have thread keep checking whether mqtt connection is alive and you can reconnect if disconnected
Suppose, Android app is detecting 3 beacons with in range with default UUID value. So, how can i set/change the name for each beacons in Anndroid (to identify each beacon)?
Appreciations for any suggestions... Thanks
Beacons are identified not only by UUID, but also by their major and minor values, and this whole set (UUID + Major + Minor) is how you can differentiate between them.
Moreover, these values are hierarchical in nature, which allows you to put some structure into your beacon deployment. Consider this example:
Whole Museum: UUID = B9407F30-F5F8-466E-AFF9-25556B57FE6D
North Wing: Major = 1
Exhibit A: Minor = 1
Exhibit B: Minor = 2
South Wing: Major = 2
This way, when your device comes in range of a beacon B9407F30-F5F8-466E-AFF9-25556B57FE6D:1:2, just by looking at it you'll know that it's at the North Wing, Exhibit B.
this is i got a solution to set different ids for different beacons. and also identifying differnet beacons and sending notifications to the user when app not in foreground.
I initially confused where to change Major and Minor values of beacons.
Simply, i installed Estimote Android app from Playstore
here,
1. Click on Beacons
2. Select one beacon and it displays one more detailed activity in that you can change Values (for me i changed Minor values for all beacon based on my requirement).
So, now Minor values for all beacons changed as per your requirement. After that find the below code
In this below code I am sending notification if App is in background or else i am dirctly updating statuses in Activity main class.
I changed Beacons values as 1,2,3,4.
public class BeaconMyService extends Service {
private static final String TAG = "BeaconMyService";
private final Handler handler = new Handler();
private BeaconManager beaconManager;
private NotificationManager notificationManager;
private static final Region ALL_ESTIMOTE_BEACONS_REGION = new Region("rid",
null, null, null);
private static final int NOTIFICATION_ID = 123;
private Messenger messageHandler;
Bundle extras;
private String notifyMsg ="";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
} catch (Exception e) {
// TODO: handle exception
}
Log.e(TAG, "Called=============");
if (beaconManager.isBluetoothEnabled()) {
connectToService();
}
return Service.START_NOT_STICKY;
}
private void connectToService() {
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
#Override
public void onServiceReady() {
try {
beaconManager.startRanging(ALL_ESTIMOTE_BEACONS_REGION);
} catch (RemoteException e) {
Log.e("Myservice",
"Cannot start ranging, something terrible happened");
Log.e("", "Cannot start ranging", e);
}
}
});
}
#Override
public void onCreate() {
super.onCreate();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
beaconManager = new BeaconManager(this);
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
#Override
public void onBeaconsDiscovered(Region region,
final List<Beacon> beacons) {
// Note that beacons reported here are already sorted by
// estimated
// distance between device and beacon.
for (int index = 0; index < beacons.size(); index++) {
Beacon beacon = beacons.get(index);
if (beacon != null) {
if (beacons.size() > 0) {
if (Utils.computeAccuracy(beacons.get(0)) < 1.0) {
try {
switch (beacons.get(0).getMinor()) {
case 1:
if (isAppInForeground(getApplicationContext())) {
Message message = Message.obtain();
message.arg1 = beacons.get(0)
.getMinor();
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
postNotification(beacons.get(0)
.getMinor()
+ ". Welcome to Media Lab");
}
break;
case 2:
if (isAppInForeground(getApplicationContext())) {
Message message = Message.obtain();
message.arg1 = beacons.get(0)
.getMinor();
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
postNotification(beacons.get(0)
.getMinor()
+ ". Welcome to Gaming Zone");
}
break;
case 3:
if (isAppInForeground(getApplicationContext())) {
Message message = Message.obtain();
message.arg1 = beacons.get(0)
.getMinor();
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
postNotification(beacons.get(0)
.getMinor()
+ ". Welcome to eLearing Education");
}
break;
case 4:
if (isAppInForeground(getApplicationContext())) {
Message message = Message.obtain();
message.arg1 = beacons.get(0)
.getMinor();
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
postNotification(beacons.get(0)
.getMinor()
+ ". Welcome to Retail Department");
}
break;
default:
break;
}
} catch (Exception e) {
// TODO: handle exception
}
}else{
if (isAppInForeground(getApplicationContext())) {
Message message = Message.obtain();
message.arg1 = 10;
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
// Utils.computeAccuracy(beacons.get(0))
}
}
}
}
}
});
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
}
// ---helper method to determine if the app is in
// the foreground---
public static boolean isAppInForeground(Context context) {
List<RunningTaskInfo> task = ((ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1);
if (task.isEmpty()) {
return false;
}
Log.e(TAG + "isAppInForeground-----",
""
+ task.get(0).topActivity.getPackageName()
.equalsIgnoreCase(context.getPackageName()));
return task.get(0).topActivity.getPackageName().equalsIgnoreCase(
context.getPackageName());
}
private void postNotification(String msg) {
if(!notifyMsg.equalsIgnoreCase(msg)){
notifyMsg = msg;
Intent notifyIntent = new Intent(BeaconMyService.this,
MainActivity.class);
notifyIntent.putExtra("content", msg);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivities(
BeaconMyService.this, 0, new Intent[] { notifyIntent },
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(
BeaconMyService.this).setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Monitoring Region").setContentText(msg)
.setAutoCancel(true).setContentIntent(pendingIntent).build();
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_LIGHTS;
notificationManager.notify(NOTIFICATION_ID, notification);
}
}
}
Coming to Activity for updating status is this
package com.hcl.beacons_notification_ex;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
static TextView tv_items;
public static Handler messageHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_items = (TextView)findViewById(R.id.tv_items);
messageHandler = new MessageHandler();
}
public static class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case 1:
tv_items.setText("Welcome to Media Lab");
break;
case 2:
tv_items.setText("Welcome to Gaming Zone");
break;
case 3:
tv_items.setText("Welcome to eLearing Education");
break;
case 4:
tv_items.setText("Welcome to Retail Department");
break;
default:
tv_items.setText("Going far to Range");
break;
}
}
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Intent i = new Intent(getApplicationContext(), BeaconMyService.class);
if(isMyServiceRunning(BeaconMyService.class)){
}else{
i.putExtra("MESSENGER", new Messenger(messageHandler));
startService(i);
// startService(i);
}
try {
if (getIntent().getExtras() != null) {
tv_items.setText(""
+ getIntent().getExtras().getString("content"));
}
} catch (Exception e) {
// TODO: handle exception
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
First of all you need estimote sdk
Then you can create a beaconDetection service something like this
public class BeaconMyService extends Service
{
private static final String TAG = "BeaconMyService";
private final Handler handler = new Handler();
private BeaconManager beaconManager;
private static final Region ALL_ESTIMOTE_BEACONS_REGION = new Region("rid", null, null, null);
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
if (beaconManager.isBluetoothEnabled())
{
connectToService();
}
return Service.START_NOT_STICKY;
}
private void connectToService()
{
beaconManager.connect(new BeaconManager.ServiceReadyCallback()
{
#Override
public void onServiceReady()
{
try
{
beaconManager.startRanging(ALL_ESTIMOTE_BEACONS_REGION);
}
catch (RemoteException e)
{
Log.e("Myservice", "Cannot start ranging, something terrible happened");
Log.e("", "Cannot start ranging", e);
}
}
});
}
#Override
public void onCreate()
{
super.onCreate();
beaconManager = new BeaconManager(this);
beaconManager.setRangingListener(new BeaconManager.RangingListener()
{
#Override
public void onBeaconsDiscovered(Region region, final List<Beacon> beacons)
{
// Note that beacons reported here are already sorted by
// estimated
// distance between device and beacon.
for (int index = 0; index < beacons.size(); index++)
{
Beacon beacon = beacons.get(index);
if (beacon != null)
{
Log.v("Beacon MacAddress", beacon.getMacAddress() + "");
if (!Constants.BEACONSDETECTEDLIST.containsKey(beacon.getMacAddress()))
{//Constants.BEACONSDETECTEDLIST is your list of beacon mac addresses
//public static HashMap<String, Long> BEACONSDETECTEDLIST = new HashMap<String, Long>();
//to check if beacon is detected for the first time
if (Constants.BEACON1.equalsIgnoreCase(beacon.getMacAddress()))
{//Constants.BEACON1 is mac address of beacon 1 assigned in constants
Constants.BEACONSDETECTEDLIST.put(beacon.getMacAddress(), System.currentTimeMillis());
handler.postDelayed(beacon1Detection, 2000);
}
else if (Constants.BEACON2.equalsIgnoreCase(beacon.getMacAddress()))
{
Constants.BEACONSDETECTEDLIST.put(beacon.getMacAddress(), System.currentTimeMillis());
handler.postDelayed(beacon2Detection, 2000);
}
}
else
{/*Do Nothing*/
}
}
}
}
});
}
private Runnable beacon1Detection = new Runnable()
{
public void run()
{
beacon1Info();
}
};
private Runnable beacon2Detection = new Runnable()
{
public void run()
{
beacon2Info();
}
};
private void beacon1Info()
{
Intent intent = new Intent(Constants.BEACON1BROADCAST_ACTION);
sendBroadcast(intent);
}//in Constants
//public static final String BEACON1BROADCAST_ACTION = "beacon1Action";
private void beacon2Info()
{
Intent intent = new Intent(Constants.BEACON2BROADCAST_ACTION);
sendBroadcast(intent);
}// in Constants
//public static final String BEACON2BROADCAST_ACTION = "beacon2Action";
#Override
public IBinder onBind(Intent intent)
{
return null;
}
#Override
public void onDestroy()
{
handler.removeCallbacks(beacon1Detection);
handler.removeCallbacks(beacon2Detection);
super.onDestroy();
}
}
And then finally you need a BeaconBroadcastReceiver to receive the broadcasts in the service and open respective Activity
public class BeaconBroadCastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Constants.BEACON1BROADCAST_ACTION)) {
Intent intent2 = new Intent(context, Beacon1Layout.class);
context.startActivity(intent2);
} else if (intent.getAction().equals(Constants.BEACON2BROADCAST_ACTION)) {
Intent intent2 = new Intent(context, Beacon2Layout.class);
context.startActivity(intent2);
}
}
}
Hope this will help you, Good luck :)
I've created a service in order to listen if I receive message from a server (PC). But it doesn't work and I don't know why. Someone can help me ?
I call my service in my main activity , in the increate() like this :
Intent intent = new Intent(this, UDPlisten.class);
startService(intent);
I don't know how I can retrieve the data sent by the PC in my main activity. Because I would like receive a notification when the server send a message to the app.
Service :
public class UDPlisten extends Service {
static String UDP_BROADCAST = "UDPBroadcast";
DatagramSocket socket;
private void listenAndWaitAndThrowIntent(int port) throws Exception {
byte[] recvBuf = new byte[15000];
if (socket == null || socket.isClosed()){
socket = new DatagramSocket(port);
socket.setBroadcast(true);
}
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
Log.e("UDP", "Waiting for UDP broadcast");
socket.receive(packet);
String message = new String(packet.getData()).trim();
Log.e("UDP", "message: " + message);
// broadcastIntent(senderIP, message);
//socket.close();
}
private void broadcastIntent(String message) {
Intent intent = new Intent(UDPlisten.UDP_BROADCAST);
intent.putExtra("message", message);
sendBroadcast(intent);
}
Thread UDPBroadcastThread;
void startListenForUDPBroadcast() {
UDPBroadcastThread = new Thread(new Runnable() {
public void run() {
try {
// InetAddress broadcastIP =InetAddress.getByName("10.0.2.16");
int port = 4000;
while (shouldRestartSocketListen) {
listenAndWaitAndThrowIntent(port);
}
//if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
} catch (Exception e) {
Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
}
}
});
UDPBroadcastThread.start();
}
private Boolean shouldRestartSocketListen=true;
void stopListen() {
shouldRestartSocketListen = false;
socket.close();
}
#Override
public void onCreate() {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
};
#Override
public void onDestroy() {
stopListen();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
Thanks in advance !
We prepared an Android application with a service that maintains MQTT connection with our server. The service returns a *START_STICKY* from its onStartCommand in order for Android to restart the service in case it kills the service for resource shortages. But the problem is, the service is killed very frequently by Android OS. It sometimes kills the service once in few seconds, even if no other process works on the device(with 2GB of ram). Why Android is killing my service so frequently? How can I lessen the number of restarts? My service should be killed as less as possible, because it disconnects my tcp connection and client have to reconnect again, causing quite a big load on our server. What can be wrong with this code? Thanks
public class GTAndroidMQTTService extends Service implements MqttCallback {
private void init() {
this.clientId = Settings.System.getString(getContentResolver(), Secure.ANDROID_ID);
}
#Override
#Deprecated
public void onStart(Intent intent, int startId) {
logger("onStart() called");
super.onStart(intent, startId);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
logger("onStartCommand() called");
if (client == null) {
try {
init();
conOpt = new MqttConnectOptions();
conOpt.setCleanSession(false);
conOpt.setUserName("...");
conOpt.setPassword("...");
try {
char[] keystorePass = getString(R.string.keystorepass).toCharArray();
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(getApplicationContext().getResources().openRawResource(R.raw.prdkey),
keystorePass);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
kmf.init(keyStore, keystorePass);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
conOpt.setSocketFactory(sslContext.getSocketFactory());
} catch (Exception ea) {
}
client = new MqttClient(this.mqttURL, clientId, new MqttDefaultFilePersistence(folder));
client.setCallback(this);
conOpt.setKeepAliveInterval(this.keepAliveSeconds);
} catch (MqttException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (intent == null) {
Log.i("TAG", "Android restarted the service[START_STICKY]");
if (client != null) {
tryToEstablishConnection();
}
}
return START_STICKY;
}
public void unsubscribe(String topicName) throws MqttException {
try {
client.unsubscribe(topicName);
} catch (Exception e) {
Log.i("TAG", "Unsubscribing from topic \"" + topicName + "has failed: " + e.toString());
}
}
private void retry() {
try {
notifyUserWithServiceStatus("Status Changed", "Status", "Connecting");
client.connect(conOpt);
notifyUserWithServiceStatus("Status Changed", "Status", "User Connected #" + (++retrycnt));
} catch (Exception e) {
notifyUserWithServiceStatus("Status Changed", "Status", "Cannot Connect");
e.printStackTrace();
}
}
public void subscribe(String topicName, int qos) throws MqttException {
try {
client.subscribe(topicName, qos);
} catch (Exception e) {
}
}
public void disconnect() {
try {
client.disconnect();
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public IBinder onBind(Intent intent) {
logger("onBind() called");
return null;
}
#Override
public void onCreate() {
logger("onCreate() called");
super.onCreate();
}
#Override
public void connectionLost(Throwable arg0) { // Connection lost
notifyUserWithServiceStatus("Status Changed", "Status", "Connection Lost!");
tryToEstablishConnection();
}
private void tryToEstablishConnection() {
if (!retrying) {
retrying = true;
new Thread(new Runnable() {
#Override
public void run() {
for (;;) {
try {
if (isOnline() && !isConnected()) {
retry();
Thread.sleep(RETRY_INTERVAL);
} else if (isConnected()) {
retrying = false;
break;
} else if (!isOnline()) {
retrying = false;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
}
private class NetworkConnectionIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
wl.acquire();
if (isOnline() && !isConnected())
notifyUserWithServiceStatus("Status Changed", "Status", "Online but not connected");
else if (!isOnline())
notifyUserWithServiceStatus("Status Changed", "Status", "Connection Lost!");
tryToEstablishConnection();
wl.release();
}
}
private boolean isConnected() {
try {
return client.isConnected();
} catch (Exception e) {
return false;
}
}
private boolean isOnline() {
ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo i = conMgr.getActiveNetworkInfo();
if (i == null)
return false;
if (!i.isConnected())
return false;
if (!i.isAvailable())
return false;
return true;
}
#Override
public void onDestroy() {
logger("onDestroy() called");
try {
client.disconnect();
Log.i("TAG", "Service stopped");
} catch (MqttException e) {
e.printStackTrace();
}
super.onDestroy();
}
#Override
public void deliveryComplete(IMqttDeliveryToken arg0) {
// TODO Auto-generated method stub
}
}
It sounds as though your service is running in the Application process; is it directly tied to your Activity?
You'll want to run it a different process entirely; you can do this by adding the following declaration in your manifest:
<service
android:name=".ServiceClassName"
android:process=":yourappname_background" >
And then use the same android:process attribute for any receiver declarations as well.
Does binding to your service keep it alive?
Some Background:
When you create a service you have to make sure your work is started in a background thread. IntentService runs on a background thread while a Service runs on the Main Thread.
A service runs in the main thread of the application that hosts it, by default
Source: http://developer.android.com/guide/components/services.html
Take a look at http://developer.android.com/guide/components/services.html#ExtendingIntentService
Read below for similar issues.
Similar answer in: Service vs IntentService
The Service can be used in tasks with no UI, but shouldn't be too long. If you need to perform long tasks, you must use threads within Service.
Also I would suggest reading CommonsWare's answer to How to always run a service in the background?
My Suggestion:
I would move to an IntentService or WakefulIntentService and consider updating information with an fixed interval instead of using a constant tcp connection. A HTTP based API could provide same information over SSL.
I ve made a Service for Socket connection, send and return, and sometimes, It receive an information and block my thread, I don't know what.
At beginning, my thread status is RUNNABLE, and once, It become a loop and I ve BLOCKED status
If anyone have an idea or a remark on my service, I m don't sur if all is very good, but that's the better way I found for send and receive sockets messages..
Thanks
public class SocketService extends Service{
private final int SERVER_PORT = 6040;
static final int MSG_ID = 0x1337;
private final String TAG = SocketService.class.getSimpleName();
private final IBinder mBinder = new SocketBinder();
private String mClientMsg;
private String serverHost = Fonctions.getTxtIp() ; // server address
private Socket socket; // socket for the connection
private ServerSocket ss = null;
private serverListenerThread listenServerThread; // thread for listening to the server
private boolean connected = false;
private PrintWriter out;
private MainActivity main;
public class SocketBinder extends Binder{
public SocketService getService(){
return SocketService.this;
}
}
/**
* Thread d'Ècoute du port SERVER_PORT
*
*/
private class serverListenerThread extends Thread implements Runnable{
private boolean end = false; // Booleen pour finir le thread
public void run(){
Log.i(TAG, "Run ServerListenerThread " + SERVER_PORT);
Socket s = null;
try {
ss = new ServerSocket(SERVER_PORT );
} catch (IOException e) {
e.printStackTrace();
}
Log.i(TAG, listenServerThread.getState().toString() );
while(!end ){
Message m = new Message();
m.what = MSG_ID;
Log.i(TAG, listenServerThread.getState().toString() );
try {
if (s == null)
s = ss.accept();
BufferedReader input = new BufferedReader(new InputStreamReader(s.getInputStream()));
String st = null;
st = input.readLine();
mClientMsg = st;
if(mClientMsg != null)
notifyMsg(mClientMsg);
Log.i(TAG, "Reception Message : " + mClientMsg);
}catch(IOException e){
Log.i(TAG, e.getMessage() );
}
catch (Exception e) {
Log.i(TAG, "connection lost" + e.getMessage() );
}
}
}
public void end(){
Log.w(TAG, "End du Thread!");
end = true;
}
}
/***
* Notifier msg
*/
private void notifyMsg(String msg)
{
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pIntent);
mNotificationManager.notify(0, mBuilder.getNotification());
}
/**
* Connection au Server pour l'envoi d'un message
* #param host
*/
public void start(String host){
new Thread(new Runnable(){
public void run() {
listenServerThread = new serverListenerThread();
listenServerThread.start();
if(! connected){
if(connectServer())
{
Log.i(TAG, "Serverhost Ca Galope");
connected = true;
Log.i(TAG, listenServerThread.getState().toString() );
}
}
else {
Log.i(TAG, "Connected : " + connected);
connected = false;
}
}
}).start();
}
/**
* Connection au Server pour l'envoi d'un message
* #return Booleen
*/
private boolean connectServer(){
try {
InetAddress serverAddr = InetAddress.getByName(serverHost);
socket = new Socket(serverAddr, SERVER_PORT);
/* out = new PrintWriter( new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true );
out.println("0|ImHere");
out.flush();*/
Log.w(TAG, "Ouverture socket");
}catch (UnknownHostException e) {
Log.i(TAG, "Erreur connectServer()" + e.getMessage() );
e.printStackTrace();
}
catch (IOException e) {
Log.i(TAG, "Erreur connectServer()" + e.getMessage() );
return false;
}
return true;
}
/**
* Envoi du message via un PrintWriter
* #param msg
*/
public void sendMsg(final String msg){
new Thread(new Runnable() {
public void run() {
Log.i(TAG, "SendMsg : " + msg + connected);
if(!connected)
connectServer();
try {
Log.i(TAG, "print writer");
out = new PrintWriter( new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true );
out.println(msg);
out.flush();
} catch (IOException e) {
e.printStackTrace() ;
Log.i(TAG, e.getMessage() );
}
}
}).start();
}
public void setMainActivity(MainActivity main)
{
this.main = main;
}
public String getMessage(){
return mClientMsg;
}
public int onStartCommand(Intent intent)
{
Log.i(TAG, "Start command Service");
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return this.mBinder;
}
/**
* Is Connected?
* #return Booleen
*/
public boolean isConnected() {
Log.i(TAG, "isConnected : " + connected);
return connected;
}
/**
* Methode appelÈe ‡ la destruction du service
*/
public void onDestroy()
{
Log.i(TAG, "Destruction Service");
super.onDestroy();
}
/**
*
*/
public void end(){
Log.i(TAG, "Deconnexion Server");
listenServerThread.end();
connected = false;
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}