下面我将全面讲解 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);
最佳实践
-
连接管理:
- 在
onResume()
中连接 - 在
onPause()
中断开连接 - 使用自动重连机制
- 在
-
主题设计:
graph TD Root-->Sensors Root-->Commands Root-->Status Sensors-->Temperature Sensors-->Humidity Commands-->LED Commands-->Motor -
QoS 选择策略:
- 控制命令:QoS 1 或 2
- 传感器数据:QoS 0 或 1
- 配置信息:QoS 2
-
后台服务:
javapublic 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; } }
-
错误处理:
javamqttClient.setCallback(new MqttCallback() { @Override public void connectionLost(Throwable cause) { if (cause instanceof MqttException) { MqttException ex = (MqttException) cause; Log.e(TAG, "Connection lost: " + ex.getReasonCode()); } // 尝试重新连接 } });
常见问题解决方案
-
连接失败:
- 检查网络权限
- 验证 Broker URL 和端口
- 测试网络可达性
-
消息丢失:
- 提高 QoS 级别
- 检查客户端 ID 唯一性
- 增加连接超时时间
-
后台限制:
xml<service android:name=".MqttService" android:foregroundServiceType="connectedDevice"/>
-
高功耗问题:
- 增加心跳间隔
- 批量发送消息
- 使用轻量级数据格式
通过以上完整实现和最佳实践,您可以在 Android 应用中高效、可靠地集成 MQTT 协议,实现设备间的实时通信。