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:
- Use publish / subscribe message mode to provide one to many message publishing and decouple applications.
- Message transmission that shields the payload content.
- Use TCP/IP to provide network connection.
- 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.
- 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:
-
download
click Download address , select the version that is most suitable for your operating system to download. As shown below: -
install
-
to configure
-
Subscribe to messages
-
Release news
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
- 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' }
- 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" />
- 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.
-
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". -
Connect the gate device to the server
Switch to the subscribe interface - set the response subject to "message_arrived" - click the Subscribe button to subscribe, as shown in the following figure: -
release
Click the Publish button in the figure in step 2 to publish -
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. -
Gate equipment receives message
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:
- MQTT 101 – How to Get Started with the lightweight IoT Protocol
- MQTT Client Library Encyclopedia – Paho Android Service
- Android APP must have advanced functions, MQTT for message push
Author: wildma
Link: https://www.jianshu.com/p/73436a5cf855