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 协议,实现设备间的实时通信。

相关推荐
叽哥8 分钟前
flutter学习第 12 节:网络请求与 JSON 解析
android·flutter·ios
y东施效颦1 小时前
uni-app app端安卓和ios如何申请麦克风权限,唤起提醒弹框
android·ios·uni-app
亲爱的非洲野猪2 小时前
从 0 到 1:用 MyCat 打造可水平扩展的 MySQL 分库分表架构
android·mysql·架构
安卓开发者2 小时前
深入理解Android Kotlin Flow:响应式编程的现代实践
android·kotlin·echarts
FunnySaltyFish2 小时前
用cursor写了个基于大模型自动提取并翻译项目字符串的工具
android·vue.js·kotlin
与火星的孩子对话3 小时前
Unity大型场景性能优化全攻略:PC与安卓端深度实践 - 场景管理、渲染优化、资源调度 C#
android·unity·性能优化·c#
_小马快跑_9 小时前
Android | Matrix.setPolyToPoly() 图像变换详解
android
tangweiguo0305198710 小时前
Flutter 与 Android NDK 集成实战:实现高性能原生功能
android·flutter
_祝你今天愉快11 小时前
Android SurfaceView & TextureView
android·性能优化