1. Kotlin实现Ble低功耗蓝牙设备连接实战指南
作为一名在Android蓝牙开发领域摸爬滚打多年的开发者,我深知Ble(低功耗蓝牙)开发中的各种坑点。今天就用一个真实项目中的仪表类多设备采集案例,带大家彻底掌握Kotlin实现Ble连接的核心技术。
不同于官方文档的抽象描述,这里我会分享实际工业场景中验证过的代码方案,包含设备连接稳定性优化、数据传输可靠性处理等实战经验。无论你是需要对接智能穿戴设备、IoT传感器还是医疗仪器,这套方案都能直接套用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Ble核心概念解析
2.1 Ble技术特点与适用场景
低功耗蓝牙(Bluetooth Low Energy)自4.0版本引入,与传统蓝牙相比有三个显著差异:
- 功耗优化:待机电流可低至0.01-0.5mA,一节纽扣电池能工作数月甚至数年
- 快速连接:建立连接仅需3ms,而经典蓝牙需要100ms以上
- 数据精简:专为间歇性小数据量传输设计(单包通常20字节)
典型应用场景包括:
- 健康监测设备(心率带、血糖仪)
- 智能家居传感器(温湿度、门磁)
- 工业现场仪表(压力表、流量计)
2.2 GATT协议深度剖析
GATT(通用属性协议)是Ble通信的基石,其层级结构就像一本书:
code复制设备(Device)
└── 章节(Service)
└── 段落(Characteristic)
├── 内容(Value)
└── 注释(Descriptor)
关键组件详解:
-
Service:
- 标准服务UUID为16位(如0x180D代表心率服务)
- 自定义服务必须使用128位UUID
- 通过
startHandle和endHandle定义服务边界
-
Characteristic:
- Properties字段决定可用操作(二进制位组合):
kotlin复制val canRead = (properties and BluetoothGattCharacteristic.PROPERTY_READ) != 0 val canNotify = (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0 - 值存储格式需与设备端约定(大端序/小端序)
- Properties字段决定可用操作(二进制位组合):
-
Descriptor:
- CCCD(客户端特征配置描述符)是最关键的描述符
- 0x2902是CCCD的标准UUID
- 写入0x0001启用通知,0x0002启用指示
实际开发中发现,约15%的国产设备不遵循CCCD标准,需要尝试写入第一个找到的描述符
3. Android权限全版本适配方案
3.1 权限声明策略
在AndroidManifest.xml中需要动态适配不同API级别:
xml复制<!-- 基础蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 6.0-10的位置权限 -->
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<!-- Android 12+的新蓝牙权限 -->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="31" />
<uses-permission
android:name="android.permission.BLUETOOTH_CONNECT"
tools:targetApi="31" />
3.2 运行时权限请求
需要分版本处理权限请求逻辑:
kotlin复制private fun checkPermissions(): Boolean {
return when {
Build.VERSION.SDK_INT >= 31 -> {
checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) == PERMISSION_GRANTED &&
checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) == PERMISSION_GRANTED
}
Build.VERSION.SDK_INT >= 23 -> {
checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PERMISSION_GRANTED
}
else -> true
}
}
private fun requestPermissio
