Using MQTT in Android mobile applications

【原文】http://dalelane.co.uk/blog/?p=1599


Overview

How to receive push notifications using MQTT in an Android mobile application

Background

I’ve written before about MQTT as a technology for doing push notifications to mobile. When I wrote that, I gave an example Android project. However, it was the first time I’d ever done Android development, and while it was an okay Java MQTT sample, it was a poor Android sample – I didn’t know anything about how Android works as a platform.

I’ve since written other Android MQTT apps, such as a hackday app forpushing updates from websites to your phone and learnt a lot about how to do it properly. Well… if not properly, at least a little better.

But Google is still directing people to my old, and probably unhelpful, sample. So it’s about time that I share something more useful.

I’ve put the full source for a sample implementation below. (Note that I’m using the Java J2EE client library from ibm.com). Hopefully the comments in it are clear enough, but here are a few of the key points.

Services vs Activities

Let’s start with an easy one. The most obvious screw-up in my first attempt was not realising the difference between Activities and Services.

When you’re writing an Android GUI, you extend Activity. Only one Activity runs on the phone at a time – the app the user is using. When you switch to another app or close the app, the Activity is trashed. If you rotate the phone while the app is running, the Activity is trashed and a new one is started to implement the GUI in the new orientation.

The point is – Activities are short-lived. Make them pretty. Don’t make them do any long-running heavy lifting.

Don’t put any of the MQTT stuff into your Activity. Because it will be killed soon, without much warning. It doesn’t matter if you kick off background threads as they’ll be killed off, too.

My first MQTT sample (which did everything in an Activity) was totally unreliable. I created an MQTTClient object which creates a long-running TCP/IP connection. And then if I turned the phone round a little bit, my MQTTClient object would get garbage collected.

Instead, I now use a Service. Services are meant for implementing longer-running operations – more than one Service can be running at a time. It doesn’t matter if you’re GUI Activity class gets trashed and garbage collected – the Service can keep running along in the background.

An MQTTClient object can survive in a Service object, and it’s persistent TCP/IP connection can be kept open.

Make it sticky

Services are meant to be long-running, but they don’t stick around forever. If the phone gets low on memory – typically because the user is using an app in the foreground (therefore is treated as high priority) which needs a lot of memory.

This happens frequently – it’s not an unusual thing that might possibly happen once a blue moon. My experience suggests that it’ll happen to your Service every day. Several times. My service might bumble along for a few hours happily, but then it’ll get killed off without warning.

By returning the START_STICKY constant when your Service is started, you tell Android that if it has to kill the Service to free up valuable resources, then you’d like it to restart the Service when resource become available again.

A persistent connection

Here’s something you get for free, but I thought was interesting enough to note.

When you create an MQTTClient object, one of the things it does is set up a long-running TCP/IP connection. If a message is received, the callback method publishArrived method is called.

When the user isn’t using their phone and has it turned off, Android starts turning stuff off to save battery life. My concern was that turning the phone off might break my connection, or stop the code that is waiting for incoming messages.

Although I haven’t found a good example of where this is documented, it seems that even if your phone is asleep, if data is received on the connection, your code will be woken up and publishArrived is called. The MQTT client library code that is blocking on the socket will revive when any data arrives on that socket.

Keeping the connection alive – in a bad way

Well… you nearly get it for free.

When you connect to the MQTT server, one of the parameters is a keepAlive period – an agreement between the client and server for how frequently the server should expect to hear from the client.

If the server doesn’t hear from the client for this keepAlive period, then it assumes that the client has gone and closes the connection.

The MQTTClient object kicks off a background thread that is responsible for sending a ping message to the server frequently enough to keep the connection alive.

I mentioned above that when the user isn’t using their phone and has it turned off, Android starts turning stuff off to save battery life. That includes the CPU. And if the CPU is stopped, this background thread stops.

Without the background thread going, after the keepAlive period expires the server closes the connection. If you’re using RSMB, you see that in the server log.

20110130 234952.683 CWNAN0024I 1200 second keepalive timeout for client 12964204954459774d56d6, ending connection

The easiest approach is to use a Wake Lock. This is a way for an application to prevent the phone from turning off. By taking aPARTIAL_WAKE_LOCK, you keep the CPU running, even if the user presses the power button.

If you take a PARTIAL_WAKE_LOCK before you do your MQTT connect, and keep it, then the CPU will not stop while your app is running, the standard MQTTClient background thread never stops, and the long running network connection is kept up.

But… erm… it also means that you’ve stopped the user from being able to turn off their phone! It’s a little aggressive, as it has a big impact on the phone’s battery life. So I prefer to avoid that.

Testing what happens when you turn your phone off – a gotcha

Some of my early attempts at an MQTT Service for Android didn’t take a wake lock, but still worked fine.

What I didn’t realise was that I had a couple of apps installed on my phone that hold partial wake locks all the time. Even though my app wasn’t stopping the phone’s CPU from turning off, other apps were. And I was getting the “benefit” of this and not realising that I had a problem.

The command adb shell dumpsys spits out a ton of stuff about the connected phone, including a list of current wake locks. (Search for ‘mLocks.size’ in the output). You have to do this a few times to get a picture of what is happening – some apps will, entirely legitimately, take a partial wake lock from time to time while they do something critical that they don’t want to be interrupted.

The problems are the apps that take a wake lock when they start, and keep it forever – the ones that are always in the mLocks table in the dumpsys output.

After uninstalling these apps, the results from my testing was very different, and was what showed me that I needed to do something to keep the connection alive.

Keeping the connection alive – in a good way

The best approach might be to write the MQTT client library to work in a more Android-centric way. But, to be honest, I’m too lazy for that – so I’m using the existing Java J2EE client library, but in addition to that I implement my own keepAlive approach, in the assumption that the client library’s one wont be good enough.

AlarmManager provides a way to ask Android to wake up a device that is asleep at a specified time, to run a specific method in your code.

I schedule something to wake up the phone before the current keepAlive period expires, long enough to send an MQTT ping, after which it is free to go back to sleep. This doesn’t wake up the phone screen if it is off – it just starts the CPU so that my keep-alive code can run.

Note that the MQTT specification says that the server should give the client a “grace period” of half the keep alive interval again (e.g. if the keep alive is 20 minutes, then the server wont cut off a client until 30 minutes after it has last heard from it). This means I don’t need to worry about getting my ping in just before the keepAlive interval – there is enough leeway in the specification that I can just schedule a ping for keepAlive seconds after the last interaction with the server.

Don’t rely on connectionLost

The MQTTClient library has a connectionLost callback that should be called to tell your code if the connection is broken. If you have a long keep alive interval, then the client can wait for a long time before considering that not hearing from the server means the connection is broken.

It’s a helpful callback, but by itself is not responsive enough, particularly for something like a phone where the connection state changes so frequently (e.g. moving out of mobile data coverage and losing the connection, moving between a wifi and a mobile cell data connection, etc.)

Android provides a notification system to inform your app of such changes. In response to these notifications, I assume that my connection is no longer reliable and re-connect.

If you’re using RSMB, you see that in the server log.

20110129 230652.613 CWNAN0033I Connection attempt to listener 1883 received from client 12963424184129774d56d6 on address 93.97.32.135:61055  
20110129 230703.834 CWNAN0033I Connection attempt to listener 1883 received from client 12963424184129774d56d6 on address 82.132.248.199:58529  
20110129 230703.834 CWNAN0034I Duplicate connection attempt received for client identifier "12963424184129774d56d6", ending oldest connection

Respect the user’s requests

screenshot - hosted by PhotobucketAndroid provides a way for the user to disable background data use by applications. But this isn’t enforced.

The user can untick the “Background data” check to indicate that applications should not use the network in the background, and provides an API for applications to check this value.

But applications are free to ignore this.

That’s not a good thing – users should be able to decide how their mobile is used.

check this value before doing anything, and set up a listener in case the value is changed while my Service is running.

Create a unique client ID

The MQTT specification requires a unique client ID for every client.

I’m using a unique device ID from the phone to create this. Annoyingly, this isn’t available on emulators, so with these I’m using a timestamp – the current time in milliseconds since Jan 1 1970.

Note that the specification also defines the maximum allowable length for a client ID, so I truncate this as necessary.

Assuming that you will spend time without a connection

This is more a general approach thing, rather than a specific bit of code. But the Service has to assume that it will frequently be disconnected, and should handle reconnecting when possible without requiring user intervention.

I’ve seen apps (I’m looking at you, facebook!) which if they fail to connect to their server, just get stuck in an endless loop of retries – constantly trying and failing.

On a personal note, I have more mobiles than SIM cards. And I notice that with such apps installed, the battery life of the phone is worse when there is no SIM card in it than when there is, in part because these apps are constantly trying to communicate with a server with no network connection.

Instead, detect when the network is not available and stop everything. Don’t poll, don’t ping, don’t keep trying or checking, just request to be notified when the connection is back, stop whatever keepalive timers are running, and sleep. And be ready to get everything back up and running quickly when a connection is available again.

There is also a consideration for how you send messages server-side. When you publish a message, there is an option to make the message ‘retained’. This means that if a client is unavailable, the server will store messages for it safely, and pass them on when the client reconnects.

This might not be suitable for all scenarios, but it’s certainly something that I use a lot, and seems to fit well with mobiles which are often disconnected, the client need not miss messages published while it’s in a black spot.

Reminding the user that you’re running

In desktop window environments, users are used to knowing what’s running on their system – because they can see the windows that they have open.

On a mobile OS, where only one app is open at a time, it’s harder to have that sense of what is running in the background. It’s okay for Services which are left running to do a specific thing, and will stop when they are complete. But for Services like this, which intend to run forever until manually stopped, the user should really be reminded that it’s there.

In my sample, I use an ongoing notification in the status bar, that will stay there whenever the Service is running. This way the user is reminded that it is using some of their phone’s resource.

You might think this could get annoying, so perhaps this should be made optional – providing the user with a way to hide the notification if they are happy to leave it running without a reminder. But even then, I would have the notification by default.

Keeping the received data safe

It’s likely that for the majority of the time that the Service is running, there wont be an Activity UI showing the latest data. The Activity UI might be started at a later time, and will want to be able to access messages that were received while it wasn’t running.

This means that the Service should probably store the messages that it receives somewhere. This will be dependent on the particular app.

Applications that handle very small amounts of data – e.g. updates and notifications that don’t need to be persisted if the app and/or phone is restarted etc. may find it acceptable to store this data in a variable in the Service. That’s what I’m doing in the sample below – storing it in a local hashtable.

Applications that handle larger amounts of data, and/or need the data to be persisted even if the app and/or phone is restarted, need to store the data somewhere safely. There are a variety of storage options available – choose the one that best suits the type / frequency / size etc. of your data.

Choosing a keep-alive value

I mentioned the keepAlive parameter above – how frequently the client should contact the server to keep the connection alive. It’s a tricky decision. Ping too frequently, and you needlessly waste battery life. If you ping too infrequently, and you might not notice if you lose your connection until the next unsuccessful attempt to ping. It’s a trade-off between how time-sensitive the data is that your app is handling, against the acceptable impact on battery life.

But there are also possibly network specific concerns? How long will a mobile network operator leave an idle connection open before they trash it? For example, it may be that there is no point having a keep alive of 40 minutes, if your user’s network operator will trash an idle connection after 20 minutes.

I need to experiment with this more, but in the meantime I’ve been using a keep alive of 20 minutes, which seems to be reliable enough.

Passing config values

A small point, but it’s worth thinking about how you give the Service the connection parameters it needs, such as the server host name. There are a variety of ways to do this.

As I don’t want the users to have to have to enter these parameters every time the Service is launched, I also needed to persist these settings. The easiest way to persist small amounts of config information in a way that is accessible to the Service and modifyable by the Activity UI is using SharedPreferences – this is how the sample below does it.

Rebroadcasting received messages for interested UIs

I’ve outlined previously the basic division between back-end, long running network connection implemented in a Service, and the UI implemented in an Activity. There are a variety of ways that these can communicate with each other.

For this sample, I use Broadcasts. It’s essentially a pub/sub engine local to the phone. Whenever the Service receives an MQTT message from the network, it re-broadcasts it locally on the phone. If an Activity UI is running, it will subscribe to these messages by creating aBroadcastReceiver. When the Activity is not active, it can unregisterthese listeners.

This means that the Service doesn’t need to keep track of whether an Activity UI is running or not – removing an element of coupling between the back and front ends of your application.

My sample Service – the code

With all of that waffling out of the way – here is the code. My blog doesn’t display code very well, so you’re probably better off copy-and-pasting it into your favourite IDE with Java colouring/formatting enabled.

I’m putting it out there in the hope that it is helpful. If you do use it, I’d appreciate a thanks and/or a link. :-)

package dalelane.android.mqtt;

import java.lang.ref.WeakReference;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;

import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
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.graphics.Color;
import android.net.ConnectivityManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.util.Log;

import com.ibm.mqtt.IMqttClient;
import com.ibm.mqtt.MqttClient;
import com.ibm.mqtt.MqttException;
import com.ibm.mqtt.MqttNotConnectedException;
import com.ibm.mqtt.MqttPersistence;
import com.ibm.mqtt.MqttPersistenceException;
import com.ibm.mqtt.MqttSimpleCallback;

/*
 * An example of how to implement an MQTT client in Android, able to receive
 *  push notifications from an MQTT message broker server.
 *  
 *  Dale Lane (dale.lane@gmail.com)
 *    28 Jan 2011
 */
public class MQTTService extends Service implements MqttSimpleCallback
{
    /************************************************************************/
    /*    CONSTANTS                                                         */
    /************************************************************************/
    
    // something unique to identify your app - used for stuff like accessing
    //   application preferences
    public static final String APP_ID = "com.dalelane.mqtt";
    
    // constants used to notify the Activity UI of received messages    
    public static final String MQTT_MSG_RECEIVED_INTENT = "com.dalelane.mqtt.MSGRECVD";
    public static final String MQTT_MSG_RECEIVED_TOPIC  = "com.dalelane.mqtt.MSGRECVD_TOPIC";
    public static final String MQTT_MSG_RECEIVED_MSG    = "com.dalelane.mqtt.MSGRECVD_MSGBODY";
    
    // constants used to tell the Activity UI the connection status
    public static final String MQTT_STATUS_INTENT = "com.dalelane.mqtt.STATUS";
    public static final String MQTT_STATUS_MSG    = "com.dalelane.mqtt.STATUS_MSG";

    // constant used internally to schedule the next ping event
    public static final String MQTT_PING_ACTION = "com.dalelane.mqtt.PING";
    
    // constants used by status bar notifications
    public static final int MQTT_NOTIFICATION_ONGOING = 1;  
    public static final int MQTT_NOTIFICATION_UPDATE  = 2;
    
    // constants used to define MQTT connection status
    public enum MQTTConnectionStatus 
    {
        INITIAL,                            // initial status
        CONNECTING,                         // attempting to connect
        CONNECTED,                          // connected
        NOTCONNECTED_WAITINGFORINTERNET,    // can't connect because the phone
                                            //     does not have Internet access
        NOTCONNECTED_USERDISCONNECT,        // user has explicitly requested 
                                            //     disconnection
        NOTCONNECTED_DATADISABLED,          // can't connect because the user 
                                            //     has disabled data access
        NOTCONNECTED_UNKNOWNREASON          // failed to connect for some reason
    }

    // MQTT constants
    public static final int MAX_MQTT_CLIENTID_LENGTH = 22;
    
    /************************************************************************/
    /*    VARIABLES used to maintain state                                  */
    /************************************************************************/
    
    // status of MQTT client connection
    private MQTTConnectionStatus connectionStatus = MQTTConnectionStatus.INITIAL;
    

    /************************************************************************/
    /*    VARIABLES used to configure MQTT connection                       */
    /************************************************************************/
    
    // taken from preferences
    //    host name of the server we're receiving push notifications from
    private String          brokerHostName       = "";
    // taken from preferences
    //    topic we want to receive messages about
    //    can include wildcards - e.g.  '#' matches anything
    private String          topicName            = "";    

    
    // defaults - this sample uses very basic defaults for it's interactions 
    //   with message brokers
    private int             brokerPortNumber     = 1883;
    private MqttPersistence usePersistence       = null;
    private boolean         cleanStart           = false;
    private int[]           qualitiesOfService   = { 0 } ;

    //  how often should the app ping the server to keep the connection alive?
    //
    //   too frequently - and you waste battery life
    //   too infrequently - and you wont notice if you lose your connection 
    //                       until the next unsuccessfull attempt to ping
    //
    //   it's a trade-off between how time-sensitive the data is that your 
    //      app is handling, vs the acceptable impact on battery life
    //
    //   it is perhaps also worth bearing in mind the network's support for 
    //     long running, idle connections. Ideally, to keep a connection open
    //     you want to use a keep alive value that is less than the period of
    //     time after which a network operator will kill an idle connection
    private short           keepAliveSeconds     = 20 * 60; 

    
    // This is how the Android client app will identify itself to the  
    //  message broker. 
    // It has to be unique to the broker - two clients are not permitted to  
    //  connect to the same broker using the same client ID. 
    private String          mqttClientId = null; 

    
    
    /************************************************************************/
    /*    VARIABLES  - other local variables                                */   
    /************************************************************************/
    // connection to the message broker
    private IMqttClient mqttClient = null;
    
    // receiver that notifies the Service when the phone gets data connection 
    private NetworkConnectionIntentReceiver netConnReceiver;
    
    // receiver that notifies the Service when the user changes data use preferences
    private BackgroundDataChangeIntentReceiver dataEnabledReceiver;
    
    // receiver that wakes the Service up when it's time to ping the server
    private PingSender pingSender;
    
    /************************************************************************/
    /*    METHODS - core Service lifecycle methods                          */
    /************************************************************************/

    // see http://developer.android.com/guide/topics/fundamentals.html#lcycles
    
    @Override
    public void onCreate() 
    {
        super.onCreate();
        
        // reset status variable to initial state
        connectionStatus = MQTTConnectionStatus.INITIAL;
        
        // create a binder that will let the Activity UI send 
        //   commands to the Service 
        mBinder = new LocalBinder<MQTTService>(this);
        
        // get the broker settings out of app preferences
        //   this is not the only way to do this - for example, you could use 
        //   the Intent that starts the Service to pass on configuration values
        SharedPreferences settings = getSharedPreferences(APP_ID, MODE_PRIVATE);
        brokerHostName = settings.getString("broker", "");
        topicName      = settings.getString("topic",  "");
        
        // register to be notified whenever the user changes their preferences 
        //  relating to background data use - so that we can respect the current
        //  preference
        dataEnabledReceiver = new BackgroundDataChangeIntentReceiver();
        registerReceiver(dataEnabledReceiver,
                         new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED));
        
        // define the connection to the broker
        defineConnectionToBroker(brokerHostName);
    }
    
    
    @Override
    public void onStart(final Intent intent, final int startId) 
    {
        // This is the old onStart method that will be called on the pre-2.0
        // platform.  On 2.0 or later we override onStartCommand() so this
        // method will not be called.        
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                handleStart(intent, startId);
            }
        }, "MQTTservice").start();
    }

    @Override
    public int onStartCommand(final Intent intent, int flags, final int startId) 
    {
        new Thread(new Runnable() {
            @Override
            public void run() {
                handleStart(intent, startId);
            }
        }, "MQTTservice").start();
        
        // return START_NOT_STICKY - we want this Service to be left running 
        //  unless explicitly stopped, and it's process is killed, we want it to
        //  be restarted
        return START_STICKY;
    }

    synchronized void handleStart(Intent intent, int startId) 
    {
        // before we start - check for a couple of reasons why we should stop
        
        if (mqttClient == null) 
        {
            // we were unable to define the MQTT client connection, so we stop 
            //  immediately - there is nothing that we can do
            stopSelf();
            return;
        }
        
        ConnectivityManager cm = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
        if (cm.getBackgroundDataSetting() == false) // respect the user's request not to use data!
        {
            // user has disabled background data
            connectionStatus = MQTTConnectionStatus.NOTCONNECTED_DATADISABLED;
            
            // update the app to show that the connection has been disabled
            broadcastServiceStatus("Not connected - background data disabled");
            
            // we have a listener running that will notify us when this 
            //   preference changes, and will call handleStart again when it 
            //   is - letting us pick up where we leave off now 
            return;
        }
        
        // the Activity UI has started the MQTT service - this may be starting
        //  the Service new for the first time, or after the Service has been
        //  running for some time (multiple calls to startService don't start
        //  multiple Services, but it does call this method multiple times)
        // if we have been running already, we re-send any stored data        
        rebroadcastStatus();
        rebroadcastReceivedMessages();
        
        // if the Service was already running and we're already connected - we 
        //   don't need to do anything 
        if (isAlreadyConnected() == false) 
        {
            // set the status to show we're trying to connect
            connectionStatus = MQTTConnectionStatus.CONNECTING;
            
            // we are creating a background service that will run forever until
            //  the user explicity stops it. so - in case they start needing 
            //  to save battery life - we should ensure that they don't forget
            //  we're running, by leaving an ongoing notification in the status
            //  bar while we are running
            NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            Notification notification = new Notification(R.drawable.icon, 
                                                         "MQTT",
                                                         System.currentTimeMillis());                                                         
            notification.flags |= Notification.FLAG_ONGOING_EVENT;
            notification.flags |= Notification.FLAG_NO_CLEAR;
            Intent notificationIntent = new Intent(this, MQTTNotifier.class);
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 
                                                                    notificationIntent, 
                                                                    PendingIntent.FLAG_UPDATE_CURRENT);
            notification.setLatestEventInfo(this, "MQTT", "MQTT Service is running", contentIntent);        
            nm.notify(MQTT_NOTIFICATION_ONGOING, notification);
            
            
            // before we attempt to connect - we check if the phone has a 
            //  working data connection
            if (isOnline())
            {
                // we think we have an Internet connection, so try to connect
                //  to the message broker
                if (connectToBroker()) 
                {
                    // we subscribe to a topic - registering to receive push
                    //  notifications with a particular key
                    // in a 'real' app, you might want to subscribe to multiple
                    //  topics - I'm just subscribing to one as an example
                    // note that this topicName could include a wildcard, so 
                    //  even just with one subscription, we could receive 
                    //  messages for multiple topics
                    subscribeToTopic(topicName);
                }
            }
            else
            {
                // we can't do anything now because we don't have a working 
                //  data connection 
                connectionStatus = MQTTConnectionStatus.NOTCONNECTED_WAITINGFORINTERNET;
                
                // inform the app that we are not connected
                broadcastServiceStatus("Waiting for network connection");
            }
        }
        
        // 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));
            
        }
        
        // creates the intents that are used to wake up the phone when it is 
        //  time to ping the server
        if (pingSender == null)
        {
            pingSender = new PingSender();
            registerReceiver(pingSender, new IntentFilter(MQTT_PING_ACTION));
        }
    }

    @Override
    public void onDestroy() 
    {
        super.onDestroy();

        // disconnect immediately
        disconnectFromBroker();

        // inform the app that the app has successfully disconnected
        broadcastServiceStatus("Disconnected");
        
        // try not to leak the listener        
        if (dataEnabledReceiver != null) 
        {
            unregisterReceiver(dataEnabledReceiver);
            dataEnabledReceiver = null;
        }
        
        if (mBinder != null) {
            mBinder.close();
            mBinder = null;
        }
    }
    
    
    /************************************************************************/
    /*    METHODS - broadcasts and notifications                            */
    /************************************************************************/
    
    // methods used to notify the Activity UI of something that has happened
    //  so that it can be updated to reflect status and the data received 
    //  from the server
    
    private void broadcastServiceStatus(String statusDescription) 
    {
        // inform the app (for times when the Activity UI is running / 
        //   active) of the current MQTT connection status so that it 
        //   can update the UI accordingly
        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(MQTT_STATUS_INTENT);
        broadcastIntent.putExtra(MQTT_STATUS_MSG, statusDescription); 
        sendBroadcast(broadcastIntent);        
    }
    
    private void broadcastReceivedMessage(String topic, String message)
    {
        // pass a message received from the MQTT server on to the Activity UI 
        //   (for times when it is running / active) so that it can be displayed 
        //   in the app GUI
        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(MQTT_MSG_RECEIVED_INTENT);
        broadcastIntent.putExtra(MQTT_MSG_RECEIVED_TOPIC, topic);
        broadcastIntent.putExtra(MQTT_MSG_RECEIVED_MSG,   message);
        sendBroadcast(broadcastIntent);                
    }
    
    // methods used to notify the user of what has happened for times when 
    //  the app Activity UI isn't running
    
    private void notifyUser(String alert, String title, String body)
    {
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Notification notification = new Notification(R.drawable.icon, alert,
                                                     System.currentTimeMillis());
        notification.defaults |= Notification.DEFAULT_LIGHTS;
        notification.defaults |= Notification.DEFAULT_SOUND;
        notification.defaults |= Notification.DEFAULT_VIBRATE;
        notification.flags |= Notification.FLAG_AUTO_CANCEL;        
        notification.ledARGB = Color.MAGENTA;
        Intent notificationIntent = new Intent(this, MQTTNotifier.class);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 
                                                                notificationIntent, 
                                                                PendingIntent.FLAG_UPDATE_CURRENT);
        notification.setLatestEventInfo(this, title, body, contentIntent);
        nm.notify(MQTT_NOTIFICATION_UPDATE, notification);        
    }
    
    
    /************************************************************************/
    /*    METHODS - binding that allows access from the Actitivy            */
    /************************************************************************/
    
    // trying to do local binding while minimizing leaks - code thanks to
    //   Geoff Bruckner - which I found at  
    //   http://groups.google.com/group/cw-android/browse_thread/thread/d026cfa71e48039b/c3b41c728fedd0e7?show_docid=c3b41c728fedd0e7
    
    private LocalBinder<MQTTService> mBinder;
    
    @Override
    public IBinder onBind(Intent intent) 
    {
        return mBinder;
    }
    public class LocalBinder<S> extends Binder 
    {
        private WeakReference<S> mService;
        
        public LocalBinder(S service)
        {
            mService = new WeakReference<S>(service);
        }
        public S getService() 
        {
            return mService.get();
        }        
        public void close() 
        { 
            mService = null; 
        }
    }
    
    // 
    // public methods that can be used by Activities that bind to the Service
    //
    
    public MQTTConnectionStatus getConnectionStatus() 
    {
        return connectionStatus;
    }    
    
    public void rebroadcastStatus()
    {
        String status = "";
        
        switch (connectionStatus)
        {
            case INITIAL:
                status = "Please wait";
                break;
            case CONNECTING:
                status = "Connecting...";
                break;
            case CONNECTED:
                status = "Connected";
                break;
            case NOTCONNECTED_UNKNOWNREASON:
                status = "Not connected - waiting for network connection";
                break;
            case NOTCONNECTED_USERDISCONNECT:
                status = "Disconnected";
                break;
            case NOTCONNECTED_DATADISABLED:
                status = "Not connected - background data disabled";
                break;
            case NOTCONNECTED_WAITINGFORINTERNET:
                status = "Unable to connect";
                break;
        }
        
        //
        // inform the app that the Service has successfully connected
        broadcastServiceStatus(status);
    }
    
    public void disconnect()
    {
        disconnectFromBroker();

        // set status 
        connectionStatus = MQTTConnectionStatus.NOTCONNECTED_USERDISCONNECT;
        
        // inform the app that the app has successfully disconnected
        broadcastServiceStatus("Disconnected");       
    }
    
    
    /************************************************************************/
    /*    METHODS - MQTT methods inherited from MQTT classes                */
    /************************************************************************/
    
    /*
     * 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)
        {
            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();
    }

    
    /*
     *   callback - called when we receive a message from the server 
     */
    public void publishArrived(String topic, byte[] payloadbytes, int qos, boolean retained)  
    {
        // 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();
        
        //
        //  I'm assuming that all messages I receive are being sent as strings
        //   this is not an MQTT thing - just me making as assumption about what
        //   data I will be receiving - your app doesn't have to send/receive
        //   strings - anything that can be sent as bytes is valid
        String messageBody = new String(payloadbytes);

        //
        //  for times when the app's Activity UI is not running, the Service 
        //   will need to safely store the data that it receives
        if (addReceivedMessageToStore(topic, messageBody))
        {
            // this is a new message - a value we haven't seen before
            
            //
            // inform the app (for times when the Activity UI is running) of the 
            //   received message so the app UI can be updated with the new data
            broadcastReceivedMessage(topic, messageBody);
            
            //
            // inform the user (for times when the Activity UI isn't running) 
            //   that there is new data available
            notifyUser("New data received", topic, messageBody);
        }
 
        // receiving this message will have kept the connection alive for us, so
        //  we take advantage of this to postpone the next scheduled ping
        scheduleNextPing();

        // we're finished - if the phone is switched off, it's okay for the CPU 
        //  to sleep now        
        wl.release();
    }

    
    /************************************************************************/
    /*    METHODS - wrappers for some of the MQTT methods that we use       */
    /************************************************************************/
    
    /*
     * Create a client connection object that defines our connection to a
     *   message broker server 
     */
    private void defineConnectionToBroker(String brokerHostName)
    {
        String mqttConnSpec = "tcp://" + brokerHostName + "@" + brokerPortNumber;
        
        try
        {
            // define the connection to the broker
            mqttClient = MqttClient.createMqttClient(mqttConnSpec, usePersistence);

            // register this client app has being able to receive messages
            mqttClient.registerSimpleHandler(this);            
        }
        catch (MqttException e)
        {
            // something went wrong!
            mqttClient = null;
            connectionStatus = MQTTConnectionStatus.NOTCONNECTED_UNKNOWNREASON;
            
            //
            // inform the app that we failed to connect so that it can update
            //  the UI accordingly
            broadcastServiceStatus("Invalid connection parameters");

            //
            // 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");
        }        
    }
    
    /*
     * (Re-)connect to the message broker
     */
    private boolean connectToBroker()
    {
        try
        {            
            // try to connect
            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;
        }
    }
    
    /*
     * Send a request to the message broker to be sent messages published with 
     *  the specified topic name. Wildcards are allowed.    
     */
    private void subscribeToTopic(String topicName)
    {
        boolean subscribed = false;
        
        if (isAlreadyConnected() == false)
        {
            // quick sanity check - don't try and subscribe if we 
            //  don't have a connection
            
            Log.e("mqtt", "Unable to subscribe as we are not connected");
        }
        else 
        {                                    
            try 
            {
                String[] topics = { topicName };
                mqttClient.subscribe(topics, qualitiesOfService);
                
                subscribed = true;
            } 
            catch (MqttNotConnectedException e) 
            {
                Log.e("mqtt", "subscribe failed - MQTT not connected", e);
            } 
            catch (IllegalArgumentException e) 
            {
                Log.e("mqtt", "subscribe failed - illegal argument", e);
            } 
            catch (MqttException e) 
            {
                Log.e("mqtt", "subscribe failed - MQTT exception", e);
            }
        }
        
        if (subscribed == false)
        {
            //
            // inform the app of the failure to subscribe so that the UI can 
            //  display an error
            broadcastServiceStatus("Unable to subscribe");

            //
            // inform the user (for times when the Activity UI isn't running)
            notifyUser("Unable to subscribe", "MQTT", "Unable to subscribe");        
        }
    }
    
    /*
     * Terminates a connection to the message broker.
     */
    private void disconnectFromBroker()
    {
        // if we've been waiting for an Internet connection, this can be 
        //  cancelled - we don't need to be told when we're connected now
        try
        {
            if (netConnReceiver != null) 
            {
                unregisterReceiver(netConnReceiver);
                netConnReceiver = null;
            }
            
            if (pingSender != null)
            {
                unregisterReceiver(pingSender);
                pingSender = null;
            }
        }
        catch (Exception eee)
        {
            // probably because we hadn't registered it
            Log.e("mqtt", "unregister failed", eee);
        }

        try 
        {
            if (mqttClient != null)
            {
                mqttClient.disconnect();
            }
        } 
        catch (MqttPersistenceException e) 
        {
            Log.e("mqtt", "disconnect failed - persistence exception", e);
        }
        finally
        {
            mqttClient = null;
        }
        
        // we can now remove the ongoing notification that warns users that
        //  there was a long-running ongoing service running
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nm.cancelAll();
    }
    
    /*
     * Checks if the MQTT client thinks it has an active connection
     */
    private boolean isAlreadyConnected()
    {
        return ((mqttClient != null) && (mqttClient.isConnected() == true));
    }
    
    private class BackgroundDataChangeIntentReceiver extends BroadcastReceiver
    {
        @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) getSystemService(POWER_SERVICE);
            WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
            wl.acquire();
   
            ConnectivityManager cm = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
            if (cm.getBackgroundDataSetting()) 
            {
                // user has allowed background data - we start again - picking 
                //  up where we left off in handleStart before
                defineConnectionToBroker(brokerHostName);
                handleStart(intent, 0);
            }
            else 
            {
                // user has disabled background data
                connectionStatus = MQTTConnectionStatus.NOTCONNECTED_DATADISABLED;
                
                // update the app to show that the connection has been disabled
                broadcastServiceStatus("Not connected - background data disabled");
                
                // disconnect from the broker
                disconnectFromBroker();
            }            
            
            // we're finished - if the phone is switched off, it's okay for the CPU 
            //  to sleep now            
            wl.release();
        }        
    }
    
    
    /*
     * 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
     */
    private class NetworkConnectionIntentReceiver extends BroadcastReceiver
    {
        @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) getSystemService(POWER_SERVICE);
            WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
            wl.acquire();
            
            if (isOnline())
            {
                // we have an internet connection - have another try at connecting
                if (connectToBroker()) 
                {
                    // we subscribe to a topic - registering to receive push
                    //  notifications with a particular key
                    subscribeToTopic(topicName);
                }
            }
            
            // we're finished - if the phone is switched off, it's okay for the CPU 
            //  to sleep now
            wl.release();
        }
    }
        

    /*
     * Schedule the next time that you want the phone to wake up and ping the 
     *  message broker server
     */
    private void scheduleNextPing()
    {
        // When the phone is off, the CPU may be stopped. This means that our 
        //   code may stop running.
        // When connecting to the message broker, we specify a 'keep alive' 
        //   period - a period after which, if the client has not contacted
        //   the server, even if just with a ping, the connection is considered
        //   broken.
        // To make sure the CPU is woken at least once during each keep alive
        //   period, we schedule a wake up to manually ping the server
        //   thereby keeping the long-running connection open
        // Normally when using this Java MQTT client library, this ping would be
        //   handled for us. 
        // Note that this may be called multiple times before the next scheduled
        //   ping has fired. This is good - the previously scheduled one will be
        //   cancelled in favour of this one.
        // This means if something else happens during the keep alive period, 
        //   (e.g. we receive an MQTT message), then we start a new keep alive
        //   period, postponing the next ping.
        
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, 
                                                                 new Intent(MQTT_PING_ACTION), 
                                                                 PendingIntent.FLAG_UPDATE_CURRENT);
        
        // in case it takes us a little while to do this, we try and do it 
        //  shortly before the keep alive period expires
        // it means we're pinging slightly more frequently than necessary 
        Calendar wakeUpTime = Calendar.getInstance();
        wakeUpTime.add(Calendar.SECOND, keepAliveSeconds);
        
        AlarmManager aMgr = (AlarmManager) getSystemService(ALARM_SERVICE);        
        aMgr.set(AlarmManager.RTC_WAKEUP,  
                 wakeUpTime.getTimeInMillis(),                 
                 pendingIntent);
    }
    
    
    /*
     * Used to implement a keep-alive protocol at this Service level - it sends 
     *  a PING message to the server, then schedules another ping after an 
     *  interval defined by keepAliveSeconds
     */
    public class PingSender extends BroadcastReceiver 
    {
        @Override
        public void onReceive(Context context, Intent intent) 
        {
            // Note that we don't need a wake lock for this method (even though
            //  it's important that the phone doesn't switch off while we're
            //  doing this).
            // According to the docs, "Alarm Manager holds a CPU wake lock as 
            //  long as the alarm receiver's onReceive() method is executing. 
            //  This guarantees that the phone will not sleep until you have 
            //  finished handling the broadcast."
            // This is good enough for our needs.
            
            try 
            {
                mqttClient.ping();                
            } 
            catch (MqttException e) 
            {
                // if something goes wrong, it should result in connectionLost
                //  being called, so we will handle it there
                Log.e("mqtt", "ping failed - MQTT exception", e);
                
                // assume the client connection is broken - trash it
                try {                    
                    mqttClient.disconnect();
                } 
                catch (MqttPersistenceException e1) {
                    Log.e("mqtt", "disconnect failed - persistence exception", e1);                
                }
                
                // reconnect
                if (connectToBroker()) {
                    subscribeToTopic(topicName);
                }                
            }

            // start the next keep alive period 
            scheduleNextPing();
        }
    }

    
    
    /************************************************************************/
    /*   APP SPECIFIC - stuff that would vary for different uses of MQTT    */
    /************************************************************************/
    
    //  apps that handle very small amounts of data - e.g. updates and 
    //   notifications that don't need to be persisted if the app / phone
    //   is restarted etc. may find it acceptable to store this data in a 
    //   variable in the Service
    //  that's what I'm doing in this sample: storing it in a local hashtable 
    //  if you are handling larger amounts of data, and/or need the data to
    //   be persisted even if the app and/or phone is restarted, then 
    //   you need to store the data somewhere safely
    //  see http://developer.android.com/guide/topics/data/data-storage.html
    //   for your storage options - the best choice depends on your needs 
    
    // stored internally
    
    private Hashtable<String, String> dataCache = new Hashtable<String, String>(); 

    private boolean addReceivedMessageToStore(String key, String value) 
    {
        String previousValue = null;
        
        if (value.length() == 0)
        {
            previousValue = dataCache.remove(key);
        }
        else
        {
            previousValue = dataCache.put(key, value);
        }
        
        // is this a new value? or am I receiving something I already knew?
        //  we return true if this is something new
        return ((previousValue == null) || 
                (previousValue.equals(value) == false));
    }

    // provide a public interface, so Activities that bind to the Service can 
    //  request access to previously received messages
    
    public void rebroadcastReceivedMessages() 
    {
        Enumeration<String> e = dataCache.keys();         
        while(e.hasMoreElements()) 
        {
            String nextKey = e.nextElement();
            String nextValue = dataCache.get(nextKey);
            
            broadcastReceivedMessage(nextKey, nextValue);
        }
    }
    

    /************************************************************************/
    /*    METHODS - internal utility methods                                */
    /************************************************************************/    
    
    private String generateClientId()
    {
        // generate a unique client id if we haven't done so before, otherwise
        //   re-use the one we already have

        if (mqttClientId == null)
        {
            // generate a unique client ID - I'm basing this on a combination of 
            //  the phone device id and the current timestamp         
            String timestamp = "" + (new Date()).getTime();
            String android_id = Settings.System.getString(getContentResolver(), 
                                                          Secure.ANDROID_ID);        
            mqttClientId = timestamp + android_id;
            
            // truncate - MQTT spec doesn't allow client ids longer than 23 chars
            if (mqttClientId.length() > MAX_MQTT_CLIENTID_LENGTH) {
                mqttClientId = mqttClientId.substring(0, MAX_MQTT_CLIENTID_LENGTH);
            }
        }
        
        return mqttClientId;
    }
    
    private boolean isOnline() 
    {
        ConnectivityManager cm = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
        if(cm.getActiveNetworkInfo() != null &&
           cm.getActiveNetworkInfo().isAvailable() &&
           cm.getActiveNetworkInfo().isConnected())
        {
            return true;
        }
        
        return false;
    }   
}

Using the Service

Finally, some comments on how to use this sample Service from your Activity.

First off, you need to make sure you have the required permissions set:

<uses-permission android:name="android.permission.INTERNET"></uses-permission> 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> 
<uses-permission android:name="android.permission.VIBRATE"></uses-permission> 
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>

To start the MQTT client, set the broker host name and topic string to subscribe to in SharedPreferences.

SharedPreferences settings = getSharedPreferences(MQTTService.APP_ID, 0);  
SharedPreferences.Editor editor = settings.edit();  
editor.putString("broker", preferenceBrokerHost);  
editor.putString("topic",  preferenceBrokerUser);	  
editor.commit();

Then set up the receivers to get the messages rebroadcasted by the Service. There are two types of messages broadcast by the Service – MQTT messages, and the current connection status.

private StatusUpdateReceiver statusUpdateIntentReceiver;  
private MQTTMessageReceiver  messageIntentReceiver;  

@Override  
public void onCreate(Bundle savedInstanceState)   
{  
    ...  

    statusUpdateIntentReceiver = new StatusUpdateReceiver();  
    IntentFilter intentSFilter = new IntentFilter(MQTTService.MQTT_STATUS_INTENT);  
    registerReceiver(statusUpdateIntentReceiver, intentSFilter);  

    messageIntentReceiver = new MQTTMessageReceiver();  
    IntentFilter intentCFilter = new IntentFilter(MQTTService.MQTT_MSG_RECEIVED_INTENT);  
    registerReceiver(messageIntentReceiver, intentCFilter);  
      
    ...  
}  

public class StatusUpdateReceiver extends BroadcastReceiver  
{  
    @Override   
    public void onReceive(Context context, Intent intent)  
    {  
        Bundle notificationData = intent.getExtras();  
        String newStatus = notificationData.getString(MQTTService.MQTT_STATUS_MSG);	    	  

        ... 
    }  
}  
public class MQTTMessageReceiver extends BroadcastReceiver  
{  
    @Override   
    public void onReceive(Context context, Intent intent)  
    {  
        Bundle notificationData = intent.getExtras();  
        String newTopic = notificationData.getString(MQTTService.MQTT_MSG_RECEIVED_TOPIC);  
        String newData  = notificationData.getString(MQTTService.MQTT_MSG_RECEIVED_MSG);	    	  

        ... 
    }  
}  

@Override  
protected void onDestroy()   
{  
    ...  

    unregisterReceiver(statusUpdateIntentReceiver);  
    unregisterReceiver(messageIntentReceiver);  

    ...  
} 

Now you’re ready to start the MQTT Service running.

Intent svc = new Intent(this, MQTTService.class);  
startService(svc); 

Similarly, to disconnect and stop:

Intent svc = new Intent(this, MQTTService.class);  
stopService(svc);

You probably also want to clear notifications created by the Service:

@Override  
public void onWindowFocusChanged(boolean hasFocus)   
{  
    super.onWindowFocusChanged(hasFocus);  
    if (hasFocus)  
    {  
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);  
        mNotificationManager.cancel(MQTTService.MQTT_NOTIFICATION_UPDATE);  
    }  
} 

Lastly, you might want to directly call methods in the Service from your Activity. Most of them are private, but you can put some public methods in there.

For example, I mentioned previously in ‘Keeping the received data safe’ that when an Activity starts, it may want to retrieve from the Service all of the pushed data it has received while it has been running.

I’ve added support to the Service for making calls like this through a local binder, which you can use like this:

bindService(new Intent(this, MQTTService.class),   
            new ServiceConnection() {                                  
                @SuppressWarnings("unchecked")  
                @Override  
                public void onServiceConnected(ComponentName className, final IBinder service)   
                {  
                    MQTTService mqttService = ((LocalBinder<MQTTService>)service).getService();                                      
                    mqttService.rebroadcastReceivedMessages();  
                    unbindService(this);  
                }  
                @Override  
                public void onServiceDisconnected(ComponentName name) {}  
            },   
            0); 

But what about… (insert alternative push framework here – e.g.Google’s Push Notification Service)?

I think this post is already long enough, right? :-)

I’ve written before about why you might want to do PUSH at all. And in this post, I’ve aimed at someone who has decided that they want to use MQTT on Android, and wants some guidance on how. I’ll leave the debate for how MQTT compares with alternative frameworks for another time.

I hope this is useful to people – please do let me know in the comments if you use it, or spot something that it could do better.



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值