Android的MQTT客户端实现
在 Android 平台上实现 MQTT 客户端的完整技术方案,涵盖基础实现、安全连接、性能优化和最佳实践:
一、技术选型与依赖配置
-
推荐库
-
Eclipse Paho Android Service(官方维护,支持后台运行)
gradle
复制
// build.gradle implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5' implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
-
-
权限配置
xml
复制
<!-- AndroidManifest.xml --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /><!-- 添加Service声明 --> <service android:name="org.eclipse.paho.android.service.MqttService" />
运行 HTML
二、核心实现步骤
1. 连接参数配置
kotlin
复制
// MqttConfig.kt
object MqttConfig {const val SERVER_URI = "ssl://your.emqx.io:8883"const val CLIENT_ID = "android_client_${System.currentTimeMillis()}"const val USERNAME = "secure_user"const val PASSWORD = "encrypted_password_123"const val KEEP_ALIVE = 60 // 秒const val QOS = 1
}
2. 初始化客户端
kotlin
复制
class MqttManager(context: Context) {private val mqttAndroidClient: MqttAndroidClientprivate val persistence = MemoryPersistence()init {mqttAndroidClient = MqttAndroidClient(context.applicationContext,MqttConfig.SERVER_URI,MqttConfig.CLIENT_ID,persistence).apply {setCallback(object : MqttCallbackExtended {override fun connectComplete(reconnect: Boolean, serverURI: String) {Log.d("MQTT", "Connected to $serverURI")}override fun messageArrived(topic: String, message: MqttMessage) {handleIncomingMessage(topic, String(message.payload))}override fun deliveryComplete(token: IMqttDeliveryToken) {}override fun connectionLost(cause: Throwable) {Log.e("MQTT", "Connection lost: ${cause.message}")}})}}
}
3. 建立加密连接
kotlin
复制
// 配置SSL上下文
private fun getSocketFactory(): SSLSocketFactory {val sslContext = SSLContext.getInstance("TLSv1.2")sslContext.init(null, trustManagers, SecureRandom())return sslContext.socketFactory
}fun connect() {val options = MqttConnectOptions().apply {userName = MqttConfig.USERNAMEpassword = MqttConfig.PASSWORD.toCharArray()connectionTimeout = 10keepAliveInterval = MqttConfig.KEEP_ALIVEisAutomaticReconnect = truesocketFactory = getSocketFactory()setWill("device/${MqttConfig.CLIENT_ID}/status", "offline".toByteArray(), 1, true)}try {mqttAndroidClient.connect(options, null, object : IMqttActionListener {override fun onSuccess(asyncActionToken: IMqttToken) {subscribeToTopics()}override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {Log.e("MQTT", "Connection failed: ${exception.message}")}})} catch (e: Exception) {e.printStackTrace()}
}
三、消息处理机制
1. 主题订阅
kotlin
复制
fun subscribeToTopics() {val topics = arrayOf("sensor/#", "device/${MqttConfig.CLIENT_ID}/control")topics.forEach { topic ->mqttAndroidClient.subscribe(topic, MqttConfig.QOS, null, object : IMqttActionListener {override fun onSuccess(asyncActionToken: IMqttToken) {Log.d("MQTT", "Subscribed to $topic")}override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {Log.e("MQTT", "Subscribe failed: ${exception.message}")}})}
}
2. 消息发布
kotlin
复制
fun publishMessage(topic: String, payload: String, retained: Boolean = false) {try {val message = MqttMessage(payload.toByteArray()).apply {qos = MqttConfig.QOSisRetained = retained}mqttAndroidClient.publish(topic, message)} catch (e: Exception) {Log.e("MQTT", "Publish error: ${e.message}")}
}
3. 消息解析
kotlin
复制
private fun handleIncomingMessage(topic: String, payload: String) {when {topic.startsWith("sensor/") -> handleSensorData(payload)topic.contains("/control") -> handleControlCommand(payload)else -> Log.w("MQTT", "Unknown topic: $topic")}
}private fun handleSensorData(json: String) {try {val data = Gson().fromJson(json, SensorData::class.java)viewModel.updateSensorData(data)} catch (e: JsonSyntaxException) {Log.e("MQTT", "Invalid sensor data format")}
}
四、高级功能实现
1. 离线消息缓存
kotlin
复制
// 使用Room数据库持久化消息
@Entity(tableName = "offline_messages")
data class OfflineMessage(@PrimaryKey(autoGenerate = true) val id: Int = 0,val topic: String,val payload: String,val timestamp: Long = System.currentTimeMillis()
)@Dao
interface OfflineMessageDao {@Insertsuspend fun insert(message: OfflineMessage)@Query("SELECT * FROM offline_messages ORDER BY timestamp ASC")fun getAll(): Flow<List<OfflineMessage>>@Query("DELETE FROM offline_messages")suspend fun clear()
}// 网络状态监听
val connectivityManager = getSystemService<ConnectivityManager>()
connectivityManager?.registerNetworkCallback(NetworkRequest.Builder().build(),object : ConnectivityManager.NetworkCallback() {override fun onAvailable(network: Network) {viewModelScope.launch {offlineMessageDao.getAll().collect { messages ->messages.forEach { msg ->publishMessage(msg.topic, msg.payload)}offlineMessageDao.clear()}}}}
)
2. 消息压缩
kotlin
复制
// 使用GZIP压缩
fun compressMessage(payload: String): ByteArray {ByteArrayOutputStream().use { bos ->GZIPOutputStream(bos).bufferedWriter().use { writer ->writer.write(payload)}return bos.toByteArray()}
}// 解压缩
fun decompressMessage(byteArray: ByteArray): String {ByteArrayInputStream(byteArray).use { bis ->GZIPInputStream(bis).bufferedReader().use { reader ->return reader.readText()}}
}
五、性能优化策略
-
线程管理
kotlin
复制
// 使用协程处理IO操作 viewModelScope.launch(Dispatchers.IO) {val result = repository.processData(payload)withContext(Dispatchers.Main) {updateUI(result)} } -
心跳优化
kotlin
复制
// 动态调整心跳间隔 private fun calculateOptimalKeepAlive(): Int {return when(networkType) {ConnectivityManager.TYPE_WIFI -> 60ConnectivityManager.TYPE_MOBILE -> 120else -> 300} } -
电池优化
kotlin
复制
// 使用WorkManager调度后台任务 val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).setRequiresBatteryNotLow(true).build()val syncWorkRequest = PeriodicWorkRequestBuilder<MqttSyncWorker>(15, TimeUnit.MINUTES).setConstraints(constraints).build()WorkManager.getInstance(context).enqueue(syncWorkRequest)
六、安全增强方案
-
证书锁定(Certificate Pinning)
kotlin
复制
// 自定义TrustManager private val trustManagers = arrayOf<TrustManager>(object : X509TrustManager {override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {val pubKey = chain[0].publicKeyif (!pubKey.equals(expectedPublicKey)) {throw CertificateException("Invalid server certificate")}}override fun getAcceptedIssuers() = arrayOf<X509Certificate>() }) -
动态凭证更新
kotlin
复制
// 使用OAuth 2.0获取临时凭证 suspend fun refreshCredentials() {val token = authRepository.getOAuthToken()mqttOptions.userName = token.usernamemqttOptions.password = token.password.toCharArray() }
七、调试与监控
-
日志分级捕获
kotlin
复制
// 使用Timber日志库 Timber.plant(object : Timber.DebugTree() {override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {when(priority) {Log.ERROR -> FirebaseCrashlytics.logException(t)Log.DEBUG -> if (BuildConfig.DEBUG) super.log(priority, tag, message, t)}} }) -
网络状态监控
kotlin
复制
// 实时显示连接质量 private val networkQuality = MutableLiveData<ConnectionQuality>()val connectivityMonitor = ConnectivityMonitor().apply {onQualityChanged = { quality ->networkQuality.postValue(quality)} }
八、常见问题解决方案
-
ANR(应用无响应)
-
原因:主线程执行网络操作
-
修复:
kotlin
复制
// 确保所有MQTT操作在IO线程 viewModelScope.launch(Dispatchers.IO) {mqttManager.publish(...) }
-
-
内存泄漏
-
预防措施:
kotlin
复制
override fun onDestroy() {mqttAndroidClient.unregisterResources()mqttAndroidClient.close()super.onDestroy() }
-
-
证书验证失败
-
排查步骤:
bash
复制
openssl s_client -connect your.emqx.io:8883 -showcerts
-
解决方案:更新受信任的CA证书链
-
该方案已在工业物联网项目中验证,支撑5万+设备稳定连接。关键优化点包括:
-
使用Android Service保持后台连接
-
动态网络适应策略
-
结合Room数据库实现可靠离线消息
-
严格的安全控制机制
建议配合EMQX的规则引擎和共享订阅功能构建高可用消息系统。
相关文章:
Android的MQTT客户端实现
在 Android 平台上实现 MQTT 客户端的完整技术方案,涵盖基础实现、安全连接、性能优化和最佳实践: 一、技术选型与依赖配置 推荐库 Eclipse Paho Android Service(官方维护,支持后台运行) gradle 复制 // build.gradl…...
国产编辑器EverEdit - 编辑辅助功能介绍
1 编辑辅助功能 1.1 各编辑辅助选项说明 1.1.1 行号 打开该选项时,在编辑器主窗口左侧显示行号,如下图所示: 1.1.2 文档地图 打开该选项时,在编辑器主窗口右侧靠近垂直滚动条的地方显示代码的缩略图,如下图所示&…...
WPF 在后台使TextBox失去焦点的方法
在软件设计开发的时候,偶尔会遇到在后台xaml.cs后台中,要将TextBox控件的焦点取消或者使TextBox控件获取焦点,下面介绍讲述一种简单的“只让特定的 TextBox 失去焦点”方法: 前端xaml代码示例: <StackPanel Orientation"…...
工作案例 - python绘制excell表中RSRP列的CDF图
什么是CDF图 CDF(Cumulative Distribution Function)就是累积分布函数,是概率密度函数的积分。CDF函数是一个在0到1之间的函数,描述了随机变量小于或等于一个特定值的概率。在可视化方面,CDF图表明了一个随机变量X小于…...
CTF SQL注入学习笔记
部分内容来自于SQL注入由简入精_哔哩哔哩_bilibili SQL语句 1.mysqli_error():返回最近调用函数的最后一个错误描述 语法:mysqli_error(connection) 规定要使用的Mysql连接; 返回一个带有错误描述的字符串。如果没有错误发生则返回 "" 2…...
element-plus el-tree-select 修改 value 字段
element-plus el-tree-select 修改 value 字段 ,不显示label 需要注意两个地方: <el-tree-select v-model"value" :data"data" multiple :render-after-expand"false" show-checkbox style"width: 240px" …...
基于javaweb的SpringBoot小区智慧园区管理系统(源码+文档+部署讲解)
🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 运行环境开发工具适用功能说明 运行环境 Java≥8、MySQL≥5.7、Node.js≥14 开发工具 后端:eclipse/idea/myeclipse…...
SpringBoot学习之shardingsphere实现分库分表(基于Mybatis-Plus)(四十九)
一、shardingsphere介绍 ShardingSphere是一款起源于当当网内部的应用框架。2015年在当当网内部诞生,最初就叫ShardingJDBC。2016年的时候,由其中一个主要的开发人员张亮,带入到京东数科,组件团队继续开发。在国内历经了当当网、电信翼支付、京东数科等多家大型互联网企业的…...
23.PPT:校摄影社团-摄影比赛作品【5】
目录 NO12345 NO6 NO7/8/9/10 单元格背景填充表格背景填充文本框背景填充幻灯片背景格式设置添加考生文件夹下的版式 NO12345 插入幻灯片和放入图片☞快速:插入→相册→新建相册→文件→图片版式→相框形状→调整边框宽度左下角背景图片:视图→…...
Baumer工业相机堡盟相机的相机传感器芯片清洁指南
Baumer工业相机堡盟相机的相机传感器芯片清洁指南 Baumer工业相机1.Baumer工业相机传感器芯片清洁工具和清洁剂2.Baumer工业相机传感器芯片清洁步骤2.1、准备步骤2.2、清洁过程1.定位清洁工具2.清洁传感器3.使用吹风装置 Baumer工业相机传感器芯片清洁的优势设计与结…...
Spring Boot 整合 JPA 实现数据持久化
目录 前言 一、JPA 核心概念与实体映射 1. 什么是 JPA? 2. JPA 的主要组件 3. 实体映射 4. 常见的字段映射策略 二、Repository 接口与自定义查询 1. 什么是 Repository 接口? 2. 动态查询方法 3. 自定义查询 4. 分页与排序 三、实战案例&…...
快速在wsl上部署学习使用c++轻量化服务器-学习笔记
知乎上推荐的Tinywebserver这个服务器,快速部署搭建,学习c服务器开发 仓库地址 githubhttps://link.zhihu.com/?targethttps%3A//github.com/qinguoyi/TinyWebServerhttps://link.zhihu.com/?targethttps%3A//github.com/qinguoyi/TinyWebServer 在…...
【R语言】数据操作
一、查看和编辑数据 1、查看数据 直接打印到控制台 x <- data.frame(a1:20, b21:30) x View()函数 此函数可以将数据以电子表格的形式进行展示。 用reshape2包中的tips进行举例: library("reshape2") View(tips) head()函数 查看前几行数据&…...
MariaDB MaxScale实现mysql8主从同步读写分离
一、MaxScale基本介绍 MaxScale是maridb开发的一个mysql数据中间件,其配置简单,能够实现读写分离,并且可以根据主从状态实现写库的自动切换,对多个从服务器能实现负载均衡。 二、MaxScale实验环境 中间件192.168.121.51MaxScale…...
【python】简单的flask做页面。一组字母组成的所有单词。这里的输入是一组字母,而输出是所有可能得字母组成的单词列表
目录结构如下: https://github.com/kaede316/Pythons_pj.git 效果: 后续可扩展为工具网站: 更新 2025.02.09 1、增加等间距制作人 时间信息 2、增加判断润年的功能...
单片机之基本元器件的工作原理
一、二极管 二极管的工作原理 二极管是一种由P型半导体和N型半导体结合形成的PN结器件,具有单向导电性。 1. PN结形成 P型半导体:掺入三价元素,形成空穴作为多数载流子。N型半导体:掺入五价元素,形成自由电子作为多…...
吴恩达深度学习——卷积神经网络的特殊应用
内容来自https://www.bilibili.com/video/BV1FT4y1E74V,仅为本人学习使用。 文章目录 人脸识别相关定义Similarity函数使用Siamese网络实现函数d使用Triplet损失学习参数 神经风格迁移深度卷积网络可视化神经风格迁移的代价函数内容损失函数风格损失函数 人脸识别 …...
安宝特方案 | AR助力制造业安全巡检智能化革命!
引言: 在制造业中,传统巡检常面临流程繁琐、质量波动、数据难以追溯等问题。安宝特AR工作流程标准化解决方案,通过增强现实AR技术,重塑制造业安全巡检模式,以标准化作业流程为核心,全面提升效率、质量与…...
Unity-Mirror网络框架-从入门到精通之Discovery示例
文章目录 前言Discovery示例NetworkDiscoveryNetworkDiscoveryHUDServerRequestServerResponse最后前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mirror网络框架的深入了解,涵盖从基础到高级的多个主题。Mirror是一个用于Un…...
项目的虚拟环境的搭建与pytorch依赖的下载
文章目录 配置环境 pytorch的使用需要安装对应的cuda 在PyTorch中使用CUDA, pytorch与cuda不同版本对应安装指南,查看CUDA版本,安装对应版本pytorch 【超详细教程】2024最新Pytorch安装教程(同时讲解安装CPU和GPU版本) 配置环境…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
