有没有办法将BLE GATT回调从异步转换为同步?

问题描述 投票:0回答:1

我是 Android 开发和并发方面的新手,我正在尝试构建一个 Kotlin 应用程序,它连接到 BLE 设备并使用

readCharacteristic
函数读取特征并使用读取的值。问题是,我希望主线程(或调用
readCharacteristic
的任何线程)阻塞并等待回调。这是因为我需要显示这个值并用于计算。

目前所有 BLE 操作都放入队列中,以便一次只有一个操作可以工作。队列的实现如下。

object ConnectionManager {
    private val deviceGattMap = ConcurrentHashMap<BluetoothDevice, BluetoothGatt>()
    private val operationQueue = ConcurrentLinkedQueue<BleOperationType>()
    private var pendingOperation: BleOperationType? = null
    private var connecting = false

    @Synchronized
    private fun enqueueOperation(operation: BleOperationType) {
        operationQueue.add(operation)
        if (pendingOperation == null) {
            doNextOperation()
        }
    }

    @Synchronized
    private fun signalEndOfOperation() {
        pendingOperation = null
        if (operationQueue.isNotEmpty()) {
            doNextOperation()
        }
    }

    @SuppressLint("MissingPermission")
    @Synchronized
    private fun doNextOperation() {
        if (pendingOperation != null) {
            Log.e(
                "ConnectionManager",
                "doNextOperation() called when an operation is pending! Aborting."
            )
            return
        }

        val operation = operationQueue.poll() ?: run {
            Log.i("ConnectionManager", "Operation queue empty, returning")
            return
        }

        pendingOperation = operation

        if (operation is Connect) {
            connecting = true
            operation.device.connectGatt(operation.context, false, gattCallback)

            while (connecting) {
            }
            return
        }

        val gatt = deviceGattMap[operation.device]
            ?: [email protected] {

                Log.e(
                    "ConnectionManager",
                    "Not connected to ${operation.device.address}! Aborting $operation operation."
                )
                signalEndOfOperation()
                return
            }

        when (operation) {

            is Disconnect -> {
                gatt.close()
                deviceGattMap.remove(operation.device)
                signalEndOfOperation()
            }

            is CharacteristicWrite -> {
                gatt.findCharacteristic(operation.characteristicUuid)?.let { characteristic ->
                    characteristic.value = operation.value
                    characteristic.writeType = operation.writeType
                    gatt.writeCharacteristic(characteristic)
                } ?: run {
                    Log.e(
                        "ConnectionManager",
                        "Characteristic ${operation.characteristicUuid} not found!"
                    )
                    signalEndOfOperation()
                }
            }

            is CharacteristicRead -> {
                gatt.findCharacteristic(operation.characteristicUuid)?.let { characteristic ->
                    gatt.readCharacteristic(characteristic)

                } ?: [email protected] {
                    Log.e(
                        "ConnectionManager",
                        "Characteristic ${operation.characteristicUuid} not found!"
                    )
                    signalEndOfOperation()
                }
            }

            is WriteDescriptor -> {
                .......
            }
        } is ......
    }

}

这是GATT回调:

@SuppressLint("MissingPermission")
    private val gattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            Log.w("BluetoothGattCallback", "Connection state changed: $status")
            if (status == BluetoothGatt.GATT_SUCCESS) {

                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    deviceGattMap[gatt.device] = gatt
                    Handler(Looper.getMainLooper()).post {
                        deviceGattMap[gatt.device]?.discoverServices()
                    }
                    connecting = false
                } else if (newState == BluetoothProfile.STATE_DISCONNECTING) {
                    disconnect(gatt.device)
                }
            } else {
                connecting = false

                Log.w("BluetoothGattCallback", "Error $status")
                if (pendingOperation is Connect) {
                    signalEndOfOperation()
                }
                //disconnect(gatt.device)
            } // Todo handle errors
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {

            with(gatt) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    Log.w("BluetoothGattCallback", "Discovered ${services.size} services")
                    if (services.isEmpty()) {
                        Log.i(
                            "PrintGattTable",
                            "No services characteristics available, Call DiscoverServices() first?"
                        )
                        return
                    }
                    else {
                        services.forEach { service ->
                            val characteristicsTable = service.characteristics.joinToString(
                                separator = "\n | --",
                                prefix = "| --"
                            ) {
                                it.uuid.toString()
                            }
                            Log.i(
                                "PrintGattTable",
                                "\n Services ${service.uuid} \n Characteristics ${characteristicsTable}"
                            )
                        }
                    }
                    ConnectionManager.requestMtu(gatt.device, 517)
                } else {
                    Log.e("BluetoothGattCallback", "Service discovery failed with status $status")
                    disconnect(device)
                }
            }
            if (pendingOperation is Connect) {
                signalEndOfOperation()
            }
        }

        override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
            Log.i("MTU", "ATT MTU changed to $mtu, success ${status == BluetoothGatt.GATT_SUCCESS}")
            if (pendingOperation is MtuRequest) {
                signalEndOfOperation()
            }
        }

        override fun onCharacteristicRead(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {

            with(characteristic) {

                when (status) {
                    BluetoothGatt.GATT_SUCCESS -> {
                        if (characteristic.uuid.toString() == BatteryCharacteristicUUID) {
                            Log.i("BluetoothGattCallback", "Read characteristic $uuid:\n${
                                value.joinToString(
                                    separator = " ",
                                    prefix = "0x"
                                ) { String.format("%02X", it) }
                            } and \n ${value[0].toUInt()}"
                            )
                        }
                        else {
                            var light : MutableList<UInt> = mutableListOf()
                            for (i in 0..value.size-1 step 4) {
                                light.add(
                                ((value[i].toUInt() and 0xFFu) shl 24) or
                                ((value[i + 1].toUInt() and 0xFFu) shl 16) or
                                ((value[i + 2].toUInt() and 0xFFu) shl 8) or
                                (value[i+ 3].toUInt() and 0xFFu))
                            }

                            Log.i("BluetoothGattCallback", "Read characteristic $uuid:\n${
                                value.joinToString(
                                    separator = " ",
                                    prefix = "0x"
                                ) { String.format("%02X", it) }
                            }" +
                                    " and \n ${light}"
                            )
                        }
                    }

                    BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
                    }
                    else -> {
                        Log.e("BluetoothGattCallback", "Characteristic read failed for $uuid, error: $status")
                    }
                }
            }
            if (pendingOperation is CharacteristicRead) {
                signalEndOfOperation()
            }
        }

    }

何时读取特征,我调用

ConnectionManager.readCharacteristic()
,它依次检查特征是否可读以及设备是否已连接,然后将操作添加到队列中。

打电话时

ConnectionManager.readCharacteristic(device, char)

Log.i("Tag", $char.value)

输出为 null,因为 Log 语句是在执行读取回调之前执行的。

有没有办法在没有时间延迟的情况下阻塞线程,或者阻塞可能不是一个好主意,应该研究涉及 future 或 Promise 的解决方案?如果是的话,欢迎任何建议!

android kotlin concurrency bluetooth-lowenergy
1个回答
0
投票

在我看来,如果可能的话,最好遵循 Google 网站建议的路线,即使用服务并通过广播更新通知活动。

请参阅 连接到 GATT 服务器

详情请参阅我的回答如何根据距离即时连接两个Android设备?

© www.soinside.com 2019 - 2024. All rights reserved.