Android MQTT 使用

下面我将全面讲解 Android 中 MQTT 的使用,包括核心概念、实现原理、完整代码示例和可视化流程图。

MQTT 核心概念

MQTT 协议特点

  • 轻量级​:适用于资源受限设备

  • 发布/订阅模式​:解耦消息生产者和消费者

  • QoS 等级​:

    • 0:最多一次(可能丢失)
    • 1:至少一次(可能重复)
    • 2:恰好一次(最可靠)

Android MQTT 架构

graph TD A[Android App] --> B[MQTT Client] B --> C[MQTT Broker] C --> D[Other Devices] C --> E[Cloud Services] D --> C E --> C

完整实现代码

1. 添加依赖 (build.gradle)

java 复制代码
dependencies {
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
}

2. AndroidManifest.xml 配置

java 复制代码
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application>
    <service android:name="org.eclipse.paho.android.service.MqttService" />
    
    <!-- 添加网络配置 -->
    <uses-library android:name="org.apache.http.legacy" android:required="false"/>
</application>

3. MQTT 管理类 (MqttManager.java)

java 复制代码
import android.content.Context;
import android.util.Log;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.*;

public class MqttManager {
    private static final String TAG = "MqttManager";
    
    private MqttAndroidClient mqttClient;
    private Context context;
    private String brokerUrl;
    private String clientId;
    private MqttCallback callback;
    
    public MqttManager(Context context, String brokerUrl, String clientId) {
        this.context = context.getApplicationContext();
        this.brokerUrl = brokerUrl;
        this.clientId = clientId;
        initClient();
    }
    
    private void initClient() {
        mqttClient = new MqttAndroidClient(context, brokerUrl, clientId);
        mqttClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                Log.i(TAG, "MQTT connected: " + (reconnect ? "reconnected" : "new connection"));
            }
            
            @Override
            public void connectionLost(Throwable cause) {
                Log.e(TAG, "MQTT connection lost", cause);
            }
            
            @Override
            public void messageArrived(String topic, MqttMessage message) {
                String payload = new String(message.getPayload());
                Log.d(TAG, "Message arrived [" + topic + "]: " + payload);
                
                // 通知UI层
                if (callback != null) {
                    callback.onMessageReceived(topic, payload);
                }
            }
            
            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                Log.d(TAG, "Message delivery complete");
            }
        });
    }
    
    public void connect(MqttConnectOptions options) {
        try {
            mqttClient.connect(options, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    Log.i(TAG, "Connected to broker");
                }
                
                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Log.e(TAG, "Connection failed", exception);
                }
            });
        } catch (MqttException e) {
            Log.e(TAG, "Error connecting to broker", e);
        }
    }
    
    public void subscribe(String topic, int qos) {
        try {
            mqttClient.subscribe(topic, qos, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    Log.i(TAG, "Subscribed to " + topic);
                }
                
                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Log.e(TAG, "Subscription failed: " + topic, exception);
                }
            });
        } catch (MqttException e) {
            Log.e(TAG, "Error subscribing to topic", e);
        }
    }
    
    public void publish(String topic, String message, int qos, boolean retained) {
        try {
            MqttMessage mqttMessage = new MqttMessage(message.getBytes());
            mqttMessage.setQos(qos);
            mqttMessage.setRetained(retained);
            mqttClient.publish(topic, mqttMessage);
        } catch (MqttException e) {
            Log.e(TAG, "Error publishing message", e);
        }
    }
    
    public void disconnect() {
        try {
            mqttClient.disconnect().setActionCallback(new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    Log.i(TAG, "Disconnected");
                }
                
                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Log.e(TAG, "Disconnect failed", exception);
                }
            });
        } catch (MqttException e) {
            Log.e(TAG, "Error disconnecting", e);
        }
    }
    
    public void setCallback(MqttCallback callback) {
        this.callback = callback;
    }
    
    public interface MqttCallback {
        void onMessageReceived(String topic, String message);
    }
}

4. 在 Activity/Fragment 中使用

java 复制代码
public class MainActivity extends AppCompatActivity implements MqttManager.MqttCallback {
    private MqttManager mqttManager;
    private static final String BROKER_URL = "tcp://mqtt.eclipseprojects.io:1883";
    private static final String CLIENT_ID = "android_client_" + UUID.randomUUID().toString();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 初始化MQTT管理器
        mqttManager = new MqttManager(this, BROKER_URL, CLIENT_ID);
        mqttManager.setCallback(this);
        
        // 配置连接选项
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        options.setAutomaticReconnect(true);
        options.setConnectionTimeout(10);
        options.setKeepAliveInterval(60);
        
        // 连接MQTT代理
        mqttManager.connect(options);
        
        // 设置UI事件
        findViewById(R.id.btn_subscribe).setOnClickListener(v -> {
            mqttManager.subscribe("sensors/temperature", 1);
        });
        
        findViewById(R.id.btn_publish).setOnClickListener(v -> {
            mqttManager.publish("commands/led", "ON", 1, false);
        });
    }
    
    @Override
    public void onMessageReceived(String topic, String message) {
        // 在主线程更新UI
        runOnUiThread(() -> {
            TextView tvMessages = findViewById(R.id.tv_messages);
            tvMessages.append(topic + ": " + message + "\n");
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mqttManager.disconnect();
    }
}

5. 布局文件 (activity_main.xml)

xml 复制代码
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">
    
    <Button
        android:id="@+id/btn_subscribe"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Subscribe to Temperature"/>
    
    <Button
        android:id="@+id/btn_publish"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Turn LED ON"/>
    
    <TextView
        android:id="@+id/tv_status"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="MQTT Status: Connecting..."/>
    
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        
        <TextView
            android:id="@+id/tv_messages"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"/>
    </ScrollView>
</LinearLayout>

MQTT 工作流程图

sequenceDiagram participant App as Android App participant Client as MQTT Client participant Broker as MQTT Broker participant Device as IoT Device App->>Client: 初始化连接参数 Client->>Broker: CONNECT Broker-->>Client: CONNACK App->>Client: SUBSCRIBE(topic) Client->>Broker: SUBSCRIBE Broker-->>Client: SUBACK loop 消息循环 Device->>Broker: PUBLISH(topic, message) Broker->>Client: PUBLISH(topic, message) Client->>App: 消息回调(onMessageReceived) end App->>Client: PUBLISH(command) Client->>Broker: PUBLISH Broker->>Device: PUBLISH(command) App->>Client: DISCONNECT Client->>Broker: DISCONNECT

高级功能实现

1. SSL/TLS 加密连接

java 复制代码
MqttConnectOptions options = new MqttConnectOptions();
try {
    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    options.setSocketFactory(sslContext.getSocketFactory());
} catch (Exception e) {
    Log.e(TAG, "SSL context error", e);
}

// 使用SSL端口连接
mqttManager = new MqttManager(this, "ssl://broker.example.com:8883", CLIENT_ID);

2. 持久化会话

java 复制代码
options.setCleanSession(false); // 设置为false以保留会话
options.setMaxInflight(100); // 设置最大飞行消息数

3. 遗嘱消息 (Last Will and Testament)

java 复制代码
options.setWill("status/device", "offline".getBytes(), 1, true);

4. 消息保留

java 复制代码
// 发布保留消息
mqttManager.publish("config/device", "default_config", 1, true);

最佳实践

  1. 连接管理​:

    • onResume() 中连接
    • onPause() 中断开连接
    • 使用自动重连机制
  2. 主题设计​:

    graph TD Root-->Sensors Root-->Commands Root-->Status Sensors-->Temperature Sensors-->Humidity Commands-->LED Commands-->Motor
  3. QoS 选择策略​:

    • 控制命令:QoS 1 或 2
    • 传感器数据:QoS 0 或 1
    • 配置信息:QoS 2
  4. 后台服务​:

    java 复制代码
    public class MqttService extends Service {
        private MqttManager mqttManager;
        
        @Override
        public void onCreate() {
            super.onCreate();
            mqttManager = new MqttManager(this, BROKER_URL, CLIENT_ID);
            // 配置并连接
        }
        
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // 处理MQTT操作
            return START_STICKY;
        }
    }
  5. 错误处理​:

    java 复制代码
    mqttClient.setCallback(new MqttCallback() {
        @Override
        public void connectionLost(Throwable cause) {
            if (cause instanceof MqttException) {
                MqttException ex = (MqttException) cause;
                Log.e(TAG, "Connection lost: " + ex.getReasonCode());
            }
            // 尝试重新连接
        }
    });

常见问题解决方案

  1. 连接失败​:

    • 检查网络权限
    • 验证 Broker URL 和端口
    • 测试网络可达性
  2. 消息丢失​:

    • 提高 QoS 级别
    • 检查客户端 ID 唯一性
    • 增加连接超时时间
  3. 后台限制​:

    xml 复制代码
    <service
        android:name=".MqttService"
        android:foregroundServiceType="connectedDevice"/>
  4. 高功耗问题​:

    • 增加心跳间隔
    • 批量发送消息
    • 使用轻量级数据格式

通过以上完整实现和最佳实践,您可以在 Android 应用中高效、可靠地集成 MQTT 协议,实现设备间的实时通信。

相关推荐
没有了遇见40 分钟前
Android 虚拟环境之虚拟环境检测<完结版>
android
liang_jy43 分钟前
Android 单元测试(二)—— 高级 Mock 技术
android·面试·单元测试
liang_jy1 小时前
Android 单元测试(一)—— 基础
android·面试·单元测试
Digitally3 小时前
如何将照片从电脑传输到安卓设备
android·电脑
教程分享大师3 小时前
创维LB2002_S905L3A处理器当贝纯净版固件下载_带root权限 白色云电脑机顶盒
android
whatever who cares3 小时前
Android Activity 任务栈详解
android
idward3073 小时前
Android的USB通信 (AOA Android开放配件协议)
android·linux
且随疾风前行.3 小时前
Android Binder 驱动 - Media 服务启动流程
android·microsoft·binder
恋猫de小郭4 小时前
Flutter 真 3D 游戏引擎来了,flame_3d 了解一下
android·前端·flutter
一笑的小酒馆4 小时前
Android使用Flow+协程封装一个FlowBus
android