Android实现MQTT客户端

java代码

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import org.eclipse.paho.android.service.MqttAndroidClient;
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.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private MqttClient mqttClient;
    private TextView connectionStatusTextView;
    private ImageView ledStatusImageView;
    private boolean isConnecting = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //使用AsyncTask异步,当执行execute时会自动开辟一个线程
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        connectionStatusTextView =(TextView) findViewById(R.id.connection_status_text_view);
        ledStatusImageView = findViewById(R.id.ledStatusImageView);

        Button connectBtn = findViewById(R.id.connectBtn);
        connectBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!isConnecting) {
                    new ConnectTask().execute();
                }
            }
        });

        Button reconnectBtn = findViewById(R.id.reconnectBtn);
        reconnectBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!isConnecting) {
                    new ReconnectTask().execute();
                }
            }
        });

        Button disconnectBtn = findViewById(R.id.disconnectBtn);
        disconnectBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new DisconnectTask().execute();
            }
        });

        Button toggleLedBtn = findViewById(R.id.toggleLED);
        toggleLedBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new ToggleLedTask().execute("led1");
            }
        });

        Button toggleLed2Btn = findViewById(R.id.toggleLED2);
        toggleLed2Btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new ToggleLedTask().execute("led2");
            }
        });
    }

    private class ConnectTask extends AsyncTask<Void, String, Boolean> {
        //MQTT连接线程,实现与服务器的连接、订阅、发布消息
        /**
         * 异步任务:AsyncTask<Params, Progress, Result>
         * 1.Params:UI线程传过来的参数。
         * 2.Progress:发布进度的类型。
         * 3.Result:返回结果的类型。耗时操作doInBackground的返回结果传给执行之后的参数类型。
         *
         * 执行流程:
         * 1.onPreExecute()
         * 2.doInBackground()-->onProgressUpdate()
         * 3.onPostExecute()
         */

        @Override
        protected void onPreExecute() //执行耗时操作之前处理UI线程事件
        {
            super.onPreExecute();
            isConnecting = true;
            connectionStatusTextView.setText("Connecting...");
        }

        @Override
        protected Boolean doInBackground(Void... voids)
        {
            //在此方法执行耗时操作,耗时操作中收发MQTT服务器的数据
            //MQTT服务器地址
            String brokerUrl = "tcp://iot.eclipse.org:1883";
            //客户端ID,用于在MQTT服务器上
            String clientId = MqttClient.generateClientId();
            try {
                mqttClient = new MqttClient(brokerUrl, clientId, new MemoryPersistence());
            } catch (MqttException e) {
                throw new RuntimeException(e);
            }
            MqttConnectOptions connectOptions = new MqttConnectOptions();
            connectOptions.setCleanSession(true);
            //mqtt服务器用户名和密码
            connectOptions.setUserName("username");
            connectOptions.setPassword("password".toCharArray());

            try {
                mqttClient.connect(connectOptions);
                mqttClient.setCallback(new MqttCallback() {
                    @Override
                    public void connectionLost(Throwable throwable) {
                        Log.d(TAG, "connectionLost: " + throwable.getMessage());
                        publishProgress("Connection lost, reconnecting...");
                        new ReconnectTask().execute();
                    }

                    @Override
                    public void messageArrived(String topic, MqttMessage message) throws Exception {
                        Log.d(TAG, "messageArrived: " + message.toString());
                        if (topic.equals("sensors/temperature")) {
                            // Update temperature reading
                            publishProgress("Temperature: " + message.toString());
                        } else if (topic.equals("sensors/humidity")) {
                            // Update humidity reading
                            publishProgress("Humidity: " + message.toString());
                        } else if (topic.equals("leds/led1/status")) {
                            // Update LED 1 status
                            if (message.toString().equals("on")) {
                                publishProgress("LED 1 is on");
                                ledStatusImageView.setImageResource(R.drawable.light_on_background);
                            } else {
                                publishProgress("LED 1 is off");
                                ledStatusImageView.setImageResource(R.drawable.light_off_background);
                            }
                        } else if (topic.equals("leds/led2/status")) {
                            // Update LED 2 status
                            if (message.toString().equals("on")) {
                                publishProgress("LED 2 is on");
                                ledStatusImageView.setImageResource(R.drawable.light_on_background);
                            } else {
                                publishProgress("LED 2 is off");
                                ledStatusImageView.setImageResource(R.drawable.light_off_background);
                            }
                        }
                    }

                    @Override
                    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                        Log.d(TAG, "deliveryComplete");
                    }
                });
                //这里是订阅的话题
                mqttClient.subscribe("sensors/temperature");
                mqttClient.subscribe("sensors/humidity");
                mqttClient.subscribe("leds/led1/status");
                mqttClient.subscribe("leds/led2/status");
            } catch (MqttException e) {
                Log.e(TAG, "Error connecting to MQTT broker: " + e.getMessage());
                publishProgress("Error connecting to MQTT broker: " + e.getMessage());
                return false;
            }
            return true;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            //用于在主线程处理doInBackground()方法执行完毕后的结果,更新UI或者执行其它操作
            super.onProgressUpdate(values);
            connectionStatusTextView.setText(values[0]);
        }

        @Override
        protected void onPostExecute(Boolean aBoolean)
        {
            //用于在主线程处理doInBackground()方法执行完毕后的结果,更新UI或者执行其它操作
            super.onPostExecute(aBoolean);
            isConnecting = false;
            if (aBoolean) {
                connectionStatusTextView.setText("Connected");
            }
        }
    }

    private class ReconnectTask extends AsyncTask<Void, String, Boolean> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            isConnecting = true;
            connectionStatusTextView.setText("Reconnecting...");
        }

        @Override
        protected Boolean doInBackground(Void... voids) {
            if (mqttClient != null && mqttClient.isConnected()) {
                try {
                    mqttClient.disconnect();
                } catch (MqttException e) {
                    Log.e(TAG, "Error disconnecting from MQTT broker: " + e.getMessage());
                }
            }
            return new ConnectTask().doInBackground();
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            isConnecting = false;
            if (aBoolean) {
                connectionStatusTextView.setText("Connected");
            }
        }
    }

    private class DisconnectTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            connectionStatusTextView.setText("Disconnecting...");
        }

        @Override
        protected Void doInBackground(Void... voids) {
            if (mqttClient != null && mqttClient.isConnected()) {
                try {
                    mqttClient.disconnect();
                } catch (MqttException e) {
                    Log.e(TAG, "Error disconnecting from MQTT broker: " + e.getMessage());
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            connectionStatusTextView.setText("Disconnected");
            ledStatusImageView.setImageResource(R.drawable.light_off_background);
        }
    }

    private class ToggleLedTask extends AsyncTask<String, Void, Void> {

        @Override
        protected Void doInBackground(String... strings) {
            if (mqttClient != null && mqttClient.isConnected()) {
                String topic = "leds/" + strings[0] + "/control";
                MqttMessage message = new MqttMessage();
                if (ledStatusImageView.getDrawable().getConstantState().equals(getResources().getDrawable(R.drawable.light_on_background).getConstantState())) {
                    message.setPayload("off".getBytes());
                } else {
                    message.setPayload("on".getBytes());
                }
                try {
                    mqttClient.publish(topic, message);
                } catch (MqttException e) {
                    Log.e(TAG, "Error publishing message: " + e.getMessage());
                }
            }
            return null;
        }
    }
}

xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 连接、重连、断开连接按钮 -->
    <Button
        android:id="@+id/connectBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Connect"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/reconnectBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Reconnect"
        android:layout_below="@id/connectBtn"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/disconnectBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Disconnect"
        android:layout_below="@id/reconnectBtn"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"/>

    <!-- 连接状态显示 -->
    <TextView
        android:id="@+id/connection_status_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Disconnected"
        android:layout_below="@id/disconnectBtn"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"/>
    <!-- 传感器数据显示 -->
    <TextView
        android:id="@+id/sensorDataTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Sensor Data:"
        android:layout_below="@id/disconnectBtn"
        android:layout_alignParentStart="true"
        android:layout_marginTop="16dp"/>

    <TextView
        android:id="@+id/sensorDataValueTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:layout_alignBaseline="@id/sensorDataTextView"
        android:layout_toEndOf="@id/sensorDataTextView"
        android:layout_marginStart="16dp"/>

    <!-- 小灯控制按钮 -->
    <Button
        android:id="@+id/toggleLED"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Toggle LED 1"
        android:layout_below="@id/sensorDataValueTextView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/toggleLED2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Toggle LED 2"
        android:layout_below="@id/toggleLED"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"/>

    <!-- 小灯状态显示 -->
    <ImageView
        android:id="@+id/ledStatusImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/light_off_background"
        android:layout_below="@id/toggleLED2"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"/>

</RelativeLayout>

声明文件Manifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mqtttest">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

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

    </application>

</manifest>

以上程序
在onCreate()方法中,初始化UI控件和MQTT客户端。其中,connectBtn按钮用于连接MQTT服务器,disconnectBtn按钮用于断开连接,reconnectBtn按钮用于重新连接。toggleLED和toggleLED2按钮用于控制两个小灯的开关。tempTextView和humidityTextView文本框用于显示传感器的温度和湿度数据,connectionStatusTextView文本框用于显示MQTT连接状态。mqttAndroidClient是一个MQTT客户端实例,用于连接MQTT服务器和进行消息的订阅和发布。

在connectBtn的onClick()方法中,调用connect()方法连接MQTT服务器,并在连接成功后订阅主题。在连接失败时,会弹出提示框提示连接失败。

在disconnectBtn的onClick()方法中,调用disconnect()方法断开MQTT服务器的连接。

在reconnectBtn的onClick()方法中,调用reconnect()方法重新连接MQTT服务器。

在toggleLED和toggleLED2的onClick()方法中,根据按钮状态发送控制指令到MQTT服务器,控制对应的小灯的开关状态。
在connect()方法中,设置MQTT客户端的各种参数,如服务器地址、客户端ID、保持连接的时间间隔、连接超时时间、是否清除会话等。然后调用connect()方法连接MQTT服务器,并设置连接成功和连接失败的回调函数。在连接成功后,订阅主题TOPIC。

在disconnect()方法中,调用MQTT客户端的disconnect()方法断开与MQTT服务器的连接。

在reconnect()方法中,判断当前是否已连接MQTT服务器。如果已连接,则先断开连接,然后重新连接MQTT服务器。

在subscribeToTopic()方法中,订阅指定主题TOPIC。当收到对应主题的消息时,会调用messageArrived()方法进行处理。

在publishMessage()方法中,向指定主题TOPIC发布指定内容的消息。

在messageArrived()方法中,处理收到的消息,更新温度和湿度数据,并根据收到的消息更新小灯的状态。
在connect()方法中,设置MQTT客户端的各种参数,如服务器地址、客户端ID、保持连接的时间间隔、连接超时时间、是否清除会话等。然后调用connect()方法连接MQTT服务器,并设置连接成功和连接失败的回调函数。在连接成功后,订阅主题TOPIC。

在disconnect()方法中,调用MQTT客户端的disconnect()方法断开与MQTT服务器的连接。

在reconnect()方法中,判断当前是否已连接MQTT服务器。如果已连接,则先断开连接,然后重新连接MQTT服务器。

在subscribeToTopic()方法中,订阅指定主题TOPIC。当收到对应主题的消息时,会调用messageArrived()方法进行处理。

在publishMessage()方法中,向指定主题TOPIC发布指定内容的消息。

在messageArrived()方法中,处理收到的消息,更新温度和湿度数据,并根据收到的消息更新小灯的状态。


版权声明:本文为Savage888777原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。