Android message push MQTT actual combat

1 Preface

At the beginning of the year, I made an Android TV application and used MQTT. The main realization is that similar scenic spots use large screens to display the number of scenic spots in real time. If the number exceeds, they are not allowed to enter. That is, use the gate equipment to monitor the tourists entering the scenic spot, and then send the message to the large screen through MQTT. Finally, the large screen displays the number of people in the scenic spot in real time, and responds to a message to inform the gate equipment that it has received the message sent by it (ensure the message arrives). This article will simulate the real use process to explain, that is, the gateway publishes the message - the server (agent) receives the message and forwards it to the large screen - the large screen responds back after receiving the message (publishes the message) - the server receives the message and forwards it to the gateway device.

2 about MQTT

2.1 introduction

MQTT (Message Queuing Telemetry Transport) is an instant messaging protocol developed by IBM. It is a publish / subscribe, extremely simple and lightweight messaging protocol designed for limited devices and low bandwidth, high latency or unreliable networks. Its design idea is light, open, simple, standardized and easy to implement. These characteristics make it a good choice for many scenarios, especially for limited environments, such as machine to machine communication (M2M) and Internet of things. Compared with XMPP, MQTT is more lightweight and occupies less broadband.

2.2 features

MQTT protocol has the following characteristics:

  1. Use publish / subscribe message mode to provide one to many message publishing and decouple applications.
  2. Message transmission that shields the payload content.
  3. Use TCP/IP to provide network connection.
  4. There are three types of quality of service for message publishing:
    • qos is 0: "at least once", and the message publishing completely depends on the underlying TCP/IP network. Message loss or duplication can occur. This level can be used in the following cases. It doesn't matter if the data of the environmental sensor is lost, because there will be a second transmission in the near future.
    • qos is 1: "at least once" to ensure that the message arrives, but message duplication may occur. This level can be used in the following situations. You need to get every message, and the repeated sending of messages has no impact on your usage scenario.
    • qos is 2: "only once", which ensures that the message arrives once. This level can be used in the following situations. In the billing system, repeated or lost messages will lead to incorrect results.
  5. Small transmission, low overhead (fixed length header is 2 bytes), and protocol switching is minimized to reduce network traffic.
    Use the Last Will and Testament features to notify the parties concerned about the mechanism of abnormal interruption of the client.

2.3 MQTT architecture

 

The architecture diagram is drawn in combination with the examples mentioned at the beginning of the article, which can well describe the three identities of MQTT in practical application. That is, a gate device is configured at the entrance of the scenic spot as the Publisher. When the gate device monitors that tourists enter, it will publish a message with Topic (for example, the Topic is "tour_enter") to the server (MQTT broker). When the server receives the published message, it will filter based on the Topic and forward the message to the subscribers who subscribe to the Topic. The large screen of the scenic spot is the Subscriber, and the subject of the subscription is also "tour_enter". In this way, you can receive the message forwarded by the server. After receiving the message, you can display the current number of scenic spots on the large screen in real time.

The gate equipment and large screen in the structure diagram are clients, which can be published and subscribed. For example, after the large screen receives the message, it can also publish a message to inform the gate device that it has received the message.

3 MQTT server setup

To use MQTT, you first need to build an MQTT server (in the company, background personnel are generally responsible for building it). In order to facilitate testing, front-end personnel will first use the server provided by the third party, official Many kinds of servers are recommended. I choose Apollo (belonging to Apache ActiveMQ) here.

1. Download and unzip

click Download address , select the version that is most suitable for your operating system to download. I use Windows here, and make the following choices:

 

 

After downloading, unzip it. Here I unzip it to the root directory of disk D (D:\apache-apollo-1.7.1).

 

2. Create a server instance

 

 

From the command line, enter the bin directory of the extracted file (for example: cd D:\apache-apollo-1.7.1\bin), and then enter apollo create mybroker (where mybroker is a custom server name) to create a server instance. See the following figure for details:

 

 

After that, the mybroker folder will be generated in the bin directory, where the etc \ apollo Under the XML file is the file for configuring server information, etc \ users The properties file contains the user name and password used when connecting to the MQTT server. Note that you can only modify the password here (it is found that many blogs say that the user name and password are modified here without authentication). If you want to modify the user name, you need to go to etc \ groups Modify the properties file. etc\groups. The user name under the properties file is the same as etc \ users The passwords under the properties file correspond one to one. As shown below, two users are configured in a group, namely admin and wildma, and the passwords corresponding to the two user names are password and 123456 respectively

 

 

3. Turn on the server

Enter the bin directory under the mybroker folder and enter Apollo broker CMD run starts the server. The following interface indicates successful startup.

 

4. Verify that the installation is successful

Finally, enter in the browser http://127.0.0.1:61680/ , if the interface can be opened successfully, the installation is successful. You can log in with the two user names configured above.

4. Debug MQTT client - use of mqttfx

In order to facilitate the debugging of MQTT, I choose mqttfx as the gateway device client. The specific use is as follows:

  1. download
    click Download address , select the version that is most suitable for your operating system to download. As shown below:

     

     

  2. install

     

    After downloading, click next all the way to install successfully. After successful installation, open the software interface. As shown below:

     

  3. to configure

     

    Click the settings in the figure above to add a new configuration file. Fill in the name of the configuration file, the address of the server (since the server is the local machine, the IP address of the local machine can be used here, and the IP address can be obtained by ipconfig/all), and the port number (the address Accepting connections at: tcp://0.0.0.0:61613 , use the port number 61613 here, see the picture after "opening the server" above), user name and password, and click OK. As shown below:

     

  4. Subscribe to messages

     

    Select the configuration file "gate device" just added and click "Connect" to Connect to the server. Click "Subscribe" to set a Topic (for example, tour_enter), and click "Subscribe" on the right side of the Topic to Subscribe to messages. As shown below:

     

  5. Release news

     

    Click "Publish", enter the Topic you just subscribed to (tour_enter), enter the message content to be published (tour enter), and click "Publish" on the right side of the Topic to Publish the message. As shown below:

     

 

 

Returning to the subscription interface, you can see the message just published, as shown in the following figure:

 

5 use of MQTT in Android

The use of MQTT in Android requires the use of Paho Android Service library, which is an MQTT client library written in Java.
GitHub address: https://github.com/eclipse/paho.mqtt.android

5.1 integration

  1. In the module's build Add dependency to gradle file

 

repositories {
    maven {
        url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
    }
}
dependencies {
    compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
    compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
}
  1. At androidmanifest XML add restriction

 

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
  1. At androidmanifest XML registration Service (MyMqttService is a Service written for itself, which will be described below)

 

        <service android:name="org.eclipse.paho.android.service.MqttService" /> <!--MqttService-->
        <service android:name="com.dongyk.service.MyMqttService"/> <!--MyMqttService-->

5.2 specific code

5.2.1 the main methods of using MQTT in Android are as follows:

  • Connect: connect to MQTT server. Here we mainly talk about the method of three parameters, as follows:

 

 @Override
 public IMqttToken connect(MqttConnectOptions options, Object userContext,
   IMqttActionListener callback) throws MqttException {
    //...
}

Parameter options: used to carry a series of parameters for connecting to the server, such as user name, password, etc.
Parameter userContext: optional object, used to pass context to callback. Generally, it can be passed to null.
Parameter callback: a callback used to monitor whether MQTT is successfully connected

  • Publish: publish a message. Here, the four parameter method is used, as follows:

 

 @Override
 public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
   boolean retained) throws MqttException, MqttPersistenceException {
    //...
 }

Parameter topic: the topic of the published message
Parameter payload: byte array of the message
Parameter qos: provides the quality of service of the message, which can be transmitted to 0, 1 or 2
Parameter retained: whether to keep the last message after disconnection in the server

  • Subscribe: subscribe to a message. Here we mainly talk about the method of two parameters, as follows:

 

 @Override
 public IMqttToken subscribe(String topic, int qos) throws MqttException,
   MqttSecurityException {
    //...
 }

Parameter topic: the topic of the subscription message
Parameter qos: the quality of service of the subscription message, which can be transmitted to 0, 1 or 2

5.2.2 MQTT Service - MyMqttService

Write a Service to realize connect, publish and subscribe of MQTT in Android

 

package com.wildma.mqttandroidclient;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.widget.Toast;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

/**
 * Author       wildma
 * Github       https://github.com/wildma
 * CreateDate   2018/11/08
 * Desc         ${MQTT Service}
 */

public class MyMqttService extends Service {

    public final String TAG = MyMqttService.class.getSimpleName();
    private static MqttAndroidClient  mqttAndroidClient;
    private        MqttConnectOptions mMqttConnectOptions;
    public        String HOST           = "tcp://192.168.0.102:61613 "; / / server address (protocol + address + port number)
    public        String USERNAME       = "admin";//user name
    public        String PASSWORD       = "password";//password
    public static String PUBLISH_TOPIC  = "tourist_enter";//Publish theme
    public static String RESPONSE_TOPIC = "message_arrived";//Response theme
    @RequiresApi(api = 26)
    public        String CLIENTID       = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
            ? Build.getSerial() : Build.SERIAL;//The client ID is generally represented by the unique identifier of the client, which is represented here by the device serial number

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        init();
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * Open service
     */
    public static void startService(Context mContext) {
        mContext.startService(new Intent(mContext, MyMqttService.class));
    }

    /**
     * Publish (simulate other clients to publish messages)
     *
     * @param message news
     */
    public static void publish(String message) {
        String topic = PUBLISH_TOPIC;
        Integer qos = 2;
        Boolean retained = false;
        try {
            //The parameters are: subject, byte array of message, quality of service, and whether to keep the last message after disconnection in the server
            mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    /**
     * Response (after receiving messages from other clients, respond to inform the other party that the message has arrived or there is a problem with the message, etc.)
     *
     * @param message news
     */
    public void response(String message) {
        String topic = RESPONSE_TOPIC;
        Integer qos = 2;
        Boolean retained = false;
        try {
            //The parameters are: subject, byte array of message, quality of service, and whether to keep the last message after disconnection in the server
            mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    /**
     * initialization
     */
    private void init() {
        String serverURI = HOST; //Server address (protocol + address + port number)
        mqttAndroidClient = new MqttAndroidClient(this, serverURI, CLIENTID);
        mqttAndroidClient.setCallback(mqttCallback); //Set the callback for listening to subscription messages
        mMqttConnectOptions = new MqttConnectOptions();
        mMqttConnectOptions.setCleanSession(true); //Sets whether to clear the cache
        mMqttConnectOptions.setConnectionTimeout(10); //Set timeout in seconds
        mMqttConnectOptions.setKeepAliveInterval(20); //Set heartbeat packet sending interval, unit: S
        mMqttConnectOptions.setUserName(USERNAME); //Set user name
        mMqttConnectOptions.setPassword(PASSWORD.toCharArray()); //Set password

        // last will message
        boolean doConnect = true;
        String message = "{\"terminal_uid\":\"" + CLIENTID + "\"}";
        String topic = PUBLISH_TOPIC;
        Integer qos = 2;
        Boolean retained = false;
        if ((!message.equals("")) || (!topic.equals(""))) {
            // The Last Will 
            try {
                mMqttConnectOptions.setWill(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
            } catch (Exception e) {
                Log.i(TAG, "Exception Occured", e);
                doConnect = false;
                iMqttActionListener.onFailure(null, e);
            }
        }
        if (doConnect) {
            doClientConnection();
        }
    }

    /**
     * Connect to MQTT server
     */
    private void doClientConnection() {
        if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) {
            try {
                mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener);
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Judge whether the network is connected
     */
    private boolean isConnectIsNomarl() {
        ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = connectivityManager.getActiveNetworkInfo();
        if (info != null && info.isAvailable()) {
            String name = info.getTypeName();
            Log.i(TAG, "Current network name:" + name);
            return true;
        } else {
            Log.i(TAG, "No network available");
            /*When there is no available network, delay 3 seconds before trying to reconnect*/
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    doClientConnection();
                }
            }, 3000);
            return false;
        }
    }

    //Listen for whether MQTT is successfully connected
    private IMqttActionListener iMqttActionListener = new IMqttActionListener() {

        @Override
        public void onSuccess(IMqttToken arg0) {
            Log.i(TAG, "Connection successful ");
            try {
                mqttAndroidClient.subscribe(PUBLISH_TOPIC, 2);//Subscription topic, parameters: topic, quality of service
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(IMqttToken arg0, Throwable arg1) {
            arg1.printStackTrace();
            Log.i(TAG, "connection failed ");
            doClientConnection();//Connection failed, reconnect (you can shut down the server for simulation)
        }
    };

    //Callback of subscription topic
    private MqttCallback mqttCallback = new MqttCallback() {

        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            Log.i(TAG, "Message received: " + new String(message.getPayload()));
            //After receiving the message, Toast will pop up here. If you need to update the UI, you can use broadcast or EventBus to send
            Toast.makeText(getApplicationContext(), "messageArrived: " + new String(message.getPayload()), Toast.LENGTH_LONG).show();
            //After receiving messages from other clients, respond to inform the other party that the message has arrived or there is a problem with the message
            response("message arrived");
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken arg0) {

        }

        @Override
        public void connectionLost(Throwable arg0) {
            Log.i(TAG, "Disconnected ");
            doClientConnection();//Disconnect, reconnect
        }
    };

    @Override
    public void onDestroy() {
        try {
            mqttAndroidClient.disconnect(); //Disconnect
        } catch (MqttException e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }
}

The general logic of the MyMqttService class is to open the service and call the init() method to initialize the various parameters, including the server address, user name, password, and so on. Then the doClientConnection() method is used to connect the MQTT server, and the iMqttActionListener is used to monitor whether the MQTT is connected successfully, and the successful connection will subscribe to the theme. mqttCallback is the callback of the subscription topic. After receiving the message, it will execute the messageArrived() method in the callback, update the UI after receiving the message, and call the response() method to inform the other party that the message has arrived or there is a problem with the message.

5.2.3 start service

Start the service in MainActivity. Here, in order to avoid UI update, the code to start the service in one line is as follows:

 

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       MyMqttService.startService(this); //Open service
   }
}

6 simulate the real scene

For the example mentioned at the beginning of the article, now take the mqttfx client as the gateway device, and the Android code above will be used as a large screen after running.

  1. Connect the large screen to the server
    The large screen APK will run on Android TV. No TV can be replaced by Android mobile phone. Remember that the release topic in the code is set to "tour_enter" and the response topic is set to "message_arrived".

  2. Connect the gate device to the server

     

    Select the gate device - click Connect - set the publishing theme to "tour_enter", as shown in the following figure:

     


    Switch to the subscribe interface - set the response subject to "message_arrived" - click the Subscribe button to subscribe, as shown in the following figure:

     

  3. release
    Click the Publish button in the figure in step 2 to publish

  4. Message received on large screen
    At this time, when the large screen receives the message forwarded by the server, it will display the number of people entering the site on the large screen and respond to inform the other party that the message has arrived. In the code, a Toast representation is played for simplicity, and the specific display is not mapped.

  5. Gate equipment receives message

     

    At this time, mqttfx switch to the Subscribe interface and you can see the response message from the large screen, as follows:

     

The above process is to roughly simulate the MQTT use process I use in development. Of course, my real project is not so simple, but also includes various data and UI interactive display. I hope that the simulation of this real use process can make you better understand the use of MQTT. Please point out the deficiencies.

Project address: MqttAndroidClient

reference material:


Author: wildma
Link: https://www.jianshu.com/p/73436a5cf855
 

Tags: MQTT

Posted by umol on Thu, 14 Apr 2022 05:27:24 +0930