Skip to content

Paho Android Service - MQTT Client Library Encyclopedia

by Sandro Kock
12 min read

The Paho Android Service is an interface to the Paho Java MQTT Client library for the Android Platform. The MQTT connection is encapsulated within an Android-Service that runs in the background of the Android application, keeping it alive when the Android application is switching between different Activities. This layer of abstraction is needed to be able to receive MQTT messages reliably.

Overview of Paho Android Service

As the Paho Android Service is based on the Paho Java client library it can be considered stable and used in production. The project is actively maintained by the Eclipse Paho project.

Pahao Android Service
Language Java
License Eclipse Public License v1.0 and Eclipse Distribution License v1.0
Website eclipse.org/paho/clients/android/
API Style Asynchronous

MQTT Features Supported by the Paho Android Service

Feature
MQTT 3.1 Yes
MQTT 3.1.1 Yes
LWT Yes
SSL/TLS No
Automatic Reconnect No (On the road map)

Advanced Features: Paho Android Service

Feature
QoS 0 Yes
QoS 1 Yes
QoS 2 Yes
Authentication Yes
Throttling No

How to Use the Paho Android Service?

Installation of Paho Android Service

Android uses Gradle as a build system and dependency management, therefor this blog post describes how the Paho Android Service can be added to an application via Gradle. The most convenient way to start a new Android Application is to use Android Studio. To add the Paho Android Service as a dependency to your app, add the following parts to your gradle file.

repositories {
    maven {
    	url "https://repo.eclipse.org/content/repositories/paho-releases/"
    }
}


dependencies {
    compile('org.eclipse.paho:org.eclipse.paho.android.service:1.0.2') {
        exclude module: 'support-v4'
    }
}

The first part adds the Paho release repository to the gradle config, so that Gradle is able to find the packaged Paho Android Service JAR. The second part adds the Paho Android Service as a dependency to the application. The exclusion of the Android Support library exclude module: 'support-v4' is only necessary if the Android application is using the Android Support Library to backport newer Android features to older Android versions. To have the latest Snapshot release within your application the gradle config below can be used.

repositories {
    maven {
    	url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
    }
}

dependencies {
    compile('org.eclipse.paho:org.eclipse.paho.android.service:1.0.3-SNAPSHOT') {
        exclude module: 'support-v4'
    }
}

Connect

As already mentioned, the Paho Android Service encapsulates the MQTT connection and offers an API for that. To be able to create a binding to the Paho Android Service, the service needs to be declared in the AndroidManifest.xml. Add the following within the <application> tag:

<service android:name="org.eclipse.paho.android.service.MqttService" >
</service>

The Paho Android Service needs the following permissions to work:

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

Add those to the <manifest> tag.

Usually an Android Activity requests or binds to an Android Service to use it, for e.g. the LocationManager to receive updates when the GPS position of the user changes. The Paho Android Service is not meant to bind to directly. Instead the provided MqttAndroidClient client interface binds to the service once created. To create and establish an MQTT-connection use the following code snipped:

String clientId = MqttClient.generateClientId();
MqttAndroidClient client =
        new MqttAndroidClient(this.getApplicationContext(), "tcp://broker.hivemq.com:1883",
                              clientId);

try {
    IMqttToken token = client.connect();
    token.setActionCallback(new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken asyncActionToken) {
            // We are connected
            Log.d(TAG, "onSuccess");
        }

        @Override
        public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
            // Something went wrong e.g. connection timeout or firewall problems
            Log.d(TAG, "onFailure");

        }
    });
} catch (MqttException e) {
    e.printStackTrace();
}

In the first line a helper Function of the Paho MQTT client is used to generate a random user id. The second line creates an instance of an Android MQTT client, that will bind to the Paho Android Service. By calling the connect method of the MqttAndroidClient the client will asynchronously try to connect to the MQTT broker and return a token. That token can be used to register callbacks, to get notified when either the MQTT-connection gets connected or an error occurs. Running the above example inside an Android Activity.onCreate method will print “onSuccess” or “onFailure” to the console.

Connect with MQTT 3.1 or MQTT 3.1.1

The MqttAndroidClient will connect with MQTT 3.1.1 by default, with a fallback to 3.1. To intentionally connect with MQTT 3.1 a MqttConnectOptions object can be supplied to the connect method:

MqttConnectOptions options = new MqttConnectOptions();
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1);
IMqttToken token = client.connect(options);

Connect with LWT

Like the MQTT version, the Last Will And Testament (LWT) can be added into the MqttConnectOptions object.

String topic = "users/last/will";
byte[] payload = "some payload".getBytes();
options.setWill(topic, payload ,1,false);
IMqttToken token = client.connect(options);

Besides the payload of the LWT, everything is straight forward and can be just set. Many MQTT clients assume that the payload is an UTF-8 formatted string and convert the payload automatically. The fact that MQTT is a binary protocol is often hidden with that. By calling String#getBytes the string will be encoded with the systems default charset (UTF-8 on Android).

Connect with Username / Password

Again like the MQTT version and the LWT, the username and password can be supplied within the options object.

options.setUserName("USERNAME");
options.setPassword("PASSWORD".toCharArray());

IMqttToken token = client.connect(options);

Publish

The MqttAndroidClient allows messages to be published via its publish(topic, MqttMessage) method. By design the MqttAndroidClient dose not queue any messages if the client is not connected and will throw an error when trying to send messages when the client is offline client.isConnected() == false. After the first connect, the IMqttActionListener.onSuccess method is called when a MQTT connection is established successfully.

String topic = "foo/bar";
String payload = "the payload";
byte[] encodedPayload = new byte[0];
try {
    encodedPayload = payload.getBytes("UTF-8");
    MqttMessage message = new MqttMessage(encodedPayload);
    client.publish(topic, message);
} catch (UnsupportedEncodingException | MqttException e) {
    e.printStackTrace();
}

Publish a Retained Message

To send a retained MQTT message the MqttMessage.retained attribute can be set to true via MqttMessage.setRetained(true).

String topic = "foo/bar";
String payload = "the payload";
byte[] encodedPayload = new byte[0];
try {
    encodedPayload = payload.getBytes("UTF-8");
    MqttMessage message = new MqttMessage(encodedPayload);
    message.setRetained(true);
    client.publish(topic, message);
} catch (UnsupportedEncodingException | MqttException e) {
    e.printStackTrace();
}

Subscribe

Subscriptions can be created via the MqttAndroidClient.subscribe method, which takes the topic and the QOS as parameters and returns a IMqttToken. The token can be used to keep track if the subscription can successfully established or failed. The example below subscribes to the topic “foo/bar” with QOS 1

String topic = "foo/bar";
int qos = 1;
try {
    IMqttToken subToken = client.subscribe(topic, qos);
    subToken.setActionCallback(new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken asyncActionToken) {
            // The message was published
        }

        @Override
        public void onFailure(IMqttToken asyncActionToken,
                              Throwable exception) {
            // The subscription could not be performed, maybe the user was not
            // authorized to subscribe on the specified topic e.g. using wildcards

        }
    });
} catch (MqttException e) {
    e.printStackTrace();
}

Unsubscribe

final String topic = "foo/bar";
try {
    IMqttToken unsubToken = client.unsubscribe(topic);
    unsubToken.setActionCallback(new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken asyncActionToken) {
            // The subscription could successfully be removed from the client
        }

        @Override
        public void onFailure(IMqttToken asyncActionToken,
                              Throwable exception) {
            // some error occurred, this is very unlikely as even if the client
            // did not had a subscription to the topic the unsubscribe action
            // will be successfully 
        }
    });
} catch (MqttException e) {
    e.printStackTrace();
}

Disconnect

To disconnect the client call the disconnect method of the MqttAndroidClient object. The example below saves the token returned by the disconnect method and adds a IMqttActionListener to get notified when the client is successfully disconnected.

try {
    IMqttToken disconToken = client.disconnect();
    disconToken.setActionCallback(new IMqttActionListener() {
        @Override
        public void onSuccess(IMqttToken asyncActionToken) {
            // we are now successfully disconnected
        }

        @Override
        public void onFailure(IMqttToken asyncActionToken,
                              Throwable exception) {
            // something went wrong, but probably we are disconnected anyway
        }
    });
} catch (MqttException e) {
    e.printStackTrace();
}

Using SSL / TLS

When connecting with SSL/TLS the MQTT-broker has to use a valid certificate that is trusted via the chain of trust of the Android device or provide a custom Java SocketFactory. Currently I am not aware of any public MQTT broker that can be used to test an SSL/TLS connection without a custom SocketFactory, hopefully this will change in the near feature with Let’s Encrypt in place.

IoT Eclipse provides an MQTT Sandbox running MQTT encrypted on iot.eclipse.org:8883 and supports TLS 1.0, 1.1 and 1.2. Android on the other hand uses a cryptography API library called BouncyCastle. To generate a BouncyCastle keystore (BKS) on a development machine the BounceCastleProvider class is required in the JAVA-Classpath. The JAR containing the class can be downloaded here. To generate the BKS the JDK keytool can be used with the following parameters:

keytool -import -alias eclipse.broker -file iot.eclipse.org.crt -keypass eclipse-password -keystore iot.eclipse.org.bks  -storetype BKS -storepass eclipse-password -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk14-1.53.jar

The command assumes that you saved the BouncyCastle JAR and the iot.eclipse.org.crt into the current directory. It will generate a BouncyCastle Keystore names iot.eclipse.org.bks with the password set to eclipse-password.

To actually use the keystore, create a folder named “assets” in the “src/main” directory of you app and copy the iot.eclipse.org.bks file into it. The following example will create a secure MQTT connection to the Eclipse test broker:

String clientId = MqttClient.generateClientId();
final MqttAndroidClient client =
        new MqttAndroidClient(this.getApplicationContext(), "ssl://iot.eclipse.org:8883",
                              clientId);
try {
    MqttConnectOptions options = new MqttConnectOptions();

        InputStream input =
                this.getApplicationContext().getAssets().open("iot.eclipse.org.bks");

        options.setSocketFactory(client.getSSLSocketFactory(input, "eclipse-password"));


        IMqttToken token = client.connect(options);
        token.setActionCallback(new IMqttActionListener() {
            @Override
            public void onSuccess(IMqttToken asyncActionToken) {
                // We are connected
                Log.d(TAG, "onSuccess");

            }

            @Override
            public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                // Something went wrong e.g. connection timeout or firewall problems
                Log.d(TAG, "onFailure");

            }
        });


} catch (MqttException | IOException e) {
    e.printStackTrace();
}

The example uses the utility function getSSLSocketFactory of the MqttAndroidClient to create a custom SocketFactory from the BKS keystore input stream.

Full Example Application of Paho Android Service

The Eclipse Paho project provides a very detailed example application that demonstrates the capabilities of the Paho Android Service. A packaged application is available at Paho Android Sample. The APK is based on the current development branch in the Eclipse repository. To clone the entire GIT repository use the following command:

git clone https://@git.eclipse.org/r/paho/org.eclipse.paho.mqtt.java
cd org.eclipse.paho.mqtt.java
git checkout develop

The Android sample application source is located in this folder, which can be opened with Android Studio.

org.eclipse.paho.android.service/org.eclipse.paho.android.sample

Sandro Kock

Sandro Kock is a mobile developer at SSV Software Systems, responsible for developing mobile solutions, with a focus on IoT/M2M. The maintainer of mqtt-elements, a set of MQTT WebComponents, created with Polymer.

  • Sandro Kock on GitHub
  • Contact Sandro Kock via e-mail

Related content:

HiveMQ logo
Review HiveMQ on G2