如何通过Kotlin Multiplatform或原生Android实现可靠BLE连接?
跨平台BLE连接实现方案(基于KMP)
一、核心设计思路
- 抽象BLE核心操作接口,彻底屏蔽各平台SDK的底层差异
- 利用KMP的
expect/actual机制实现平台专属逻辑,保证上层调用一致性 - 封装扫描、连接、数据交互、通知等通用流程,降低业务层的开发复杂度
二、具体实现步骤
1. 定义通用BLE接口(Common模块)
// common/src/commonMain/kotlin/com/example/ble/BleManager.kt expect class BleManager { fun startScan(callback: (BleDevice) -> Unit) fun stopScan() suspend fun connect(deviceId: String): Result<Unit> suspend fun writeCharacteristic(serviceUuid: String, charUuid: String, data: ByteArray): Result<Unit> suspend fun readCharacteristic(serviceUuid: String, charUuid: String): Result<ByteArray> fun setCharacteristicNotification(serviceUuid: String, charUuid: String, enabled: Boolean, callback: (ByteArray) -> Unit) fun disconnect() } data class BleDevice(val id: String, val name: String?)
2. Android平台实现(Android模块)
// android/src/androidMain/kotlin/com/example/ble/BleManager.kt actual class BleManager actual constructor() { private val bluetoothManager = ContextCompat.getSystemService( ApplicationProvider.getApplicationContext(), BluetoothManager::class.java )!! private val bluetoothAdapter = bluetoothManager.adapter private var scanCallback: ScanCallback? = null private var gatt: BluetoothGatt? = null private var notificationCallback: ((ByteArray) -> Unit)? = null actual fun startScan(callback: (BleDevice) -> Unit) { if (!bluetoothAdapter.isEnabled) return scanCallback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { super.onScanResult(callbackType, result) val device = BleDevice(result.device.address, result.device.name) callback(device) } } bluetoothAdapter.bluetoothLeScanner.startScan(scanCallback) } actual fun stopScan() { scanCallback?.let { bluetoothAdapter.bluetoothLeScanner.stopScan(it) } scanCallback = null } actual suspend fun connect(deviceId: String): Result<Unit> = suspendCancellableCoroutine { cont -> val device = bluetoothAdapter.getRemoteDevice(deviceId) gatt = device.connectGatt( ApplicationProvider.getApplicationContext(), false, object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { super.onConnectionStateChange(gatt, status, newState) if (newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverServices() } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { cont.resume(Result.failure(IllegalStateException("Connection failed"))) } } override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { super.onServicesDiscovered(gatt, status) if (status == BluetoothGatt.GATT_SUCCESS) { cont.resume(Result.success(Unit)) } else { cont.resume(Result.failure(IllegalStateException("Discover services failed"))) } } } ) } // 读写特征值、通知注册等方法可参照上述逻辑,基于BluetoothGatt实现 }
3. 其他平台适配(如iOS)
在KMP的iOS模块中,基于CoreBluetooth框架实现对应的actual方法,保持接口调用逻辑与Android端完全对齐,无需修改业务代码即可跨平台运行。
三、关键优化点
- 连接稳定性:实现自动重连机制,处理蓝牙断开、信号弱、系统资源回收等异常场景
- 扫描优化:对扫描结果去重,添加扫描时长限制,避免无效设备重复回调
- 线程安全:将所有BLE操作切换至专用线程(Android用
HandlerThread,iOS用DispatchQueue),避免主线程阻塞 - 资源管理:在断开连接或销毁实例时,及时释放
BluetoothGatt、ScanCallback等资源,防止内存泄漏
四、业务层使用示例
// 业务代码中直接调用通用接口,无需关心平台差异 val bleManager = BleManager() val scope = CoroutineScope(Dispatchers.IO) // 扫描BLE设备 bleManager.startScan { device -> println("Found device: ${device.name ?: "Unnamed"} (${device.id})") } // 连接指定设备 scope.launch { val connectResult = bleManager.connect("AA:BB:CC:DD:EE:FF") if (connectResult.isSuccess) { // 读取特征值 val readResult = bleManager.readCharacteristic("SERVICE_UUID", "CHAR_UUID") readResult.onSuccess { data -> println("Read data: ${data.decodeToString()}") } // 注册特征值通知 bleManager.setCharacteristicNotification("SERVICE_UUID", "CHAR_UUID", true) { data -> println("Received notification: ${data.decodeToString()}") } } }
内容的提问来源于stack exchange,提问作者mxkmn
相关产品推荐
相关产品推荐

