Android BLE write Characteristic 锁定 onCharacteristicWrite/onCharacteristicChange

Posted

技术标签:

【中文标题】Android BLE write Characteristic 锁定 onCharacteristicWrite/onCharacteristicChange【英文标题】:Android BLE write Characteristic locks up onCharacteristicWrite/onCharacteristicChange 【发布时间】:2019-10-11 00:59:48 【问题描述】:

我有一个用于发送消息缓冲区的消息线程。在特征写入下一条消息之前,每条消息都会排队等待onCharacteristicWrite 成功发送。特征也设置为WRITE_TYPE_NO_RESPONSE,因此消息缓冲区队列在特征写入调用之间非常快(大约 0-7 毫秒)。

主要问题:“卡住”特性

在大多数情况下,这很有效。当有大量消息时,似乎会出现问题(可能在消息较少时发生,但在发送大量消息时更明显)。发生的情况是 writeCharacteristic 将被调用,并且特性似乎被锁定,因为 onCharacteristicChanged 不再读取任何新数据,也无法访问 onCharacteristicWrite

我注意到的其他事项:

    在每个 characteristicWrite 之后添加 5-10 毫秒的睡眠延迟似乎 帮助,但我不明白为什么当onCharacteristicWrite 返回成功时蓝牙 GATT 对象需要延迟。

    有时我会在onConnectionStateChange 收到回调,状态为 8,设备超出范围。不过,这并不总是发生。

    有时characteristicWrite 返回假;但是,它也可以在进入上述“阻塞特性”状态之前返回 true

消息线程代码:

    private boolean stopMessageThread = false;
    private boolean characteristicWriteSuccessful = true;
    private ArrayList<byte[]> messageQueue = new ArrayList<byte[]>();
    private Thread messageThread =  new Thread( new Runnable() 
        private long lastTime = 0;
        private int count = 0;
        @Override
        public void run() 
            while (!Thread.currentThread().isInterrupted() && !stopMessageThread) 
                if(messageQueue.size() != 0 && characteristicWriteSuccessful) 

                    Log.i(TAG, ""+(System.currentTimeMillis()-lastTime));
                    Log.i(TAG, "Queue count: "+messageQueue.size());

                    characteristicWriteSuccessful = false;
                    byte[] message = messageQueue.remove(0);
                    customCharacteristic.setValue(message);
                    boolean status = bluetoothGatt.writeCharacteristic(customCharacteristic);

                    Log.i(TAG, "write characteristic status "+status);

                    lastTime = System.currentTimeMillis();
                    //sleep(10); // this kinda helps but can still throw the error
                
            
        
    );

【问题讨论】:

首先,请不要通过繁忙的循环来最大化 CPU。二、onCharacteristicChanged在这里起什么作用?第三,onCharacteristicWrite 不能“返回”false,因为它是一个回调。四、你的characteristicWriteSuccessful 必须是volatile。 @Emil 当然,我将更改繁忙循环,第二个,onCharacteristicChanged 从我的设备接收数据,当我收到此错误时不再这样做。第三,我的意思是“characteristicWrite”函数,而不是回调。我也会更改布尔值。 writeCharacteristic 在已经有另一个挂起的 GATT 操作时返回 false。在此写入线程运行时,您是否执行任何其他请求?对于 onCharacteristicChanged,它是您收到通知的另一个特征吗? 没有其他请求。与 oncharacteristicChanged 相同,“customCharacteristic”。 由于 android ***.com/questions/38922639/… 中的竞争条件,我建议具有两个特征。 writeCharacteristic 通常返回 false,因为您还有另一个请求待处理 android.googlesource.com/platform/frameworks/base/+/master/core/… 【参考方案1】:

除了忙等待会阻塞整个 CPU 并迅速耗尽电池,我看不到任何同步。有共享数据结构(可能是stopMessageThreadcharacteristicWriteSuccessfulmessageQueue)和多个线程访问它们。如果没有同步,就会出现竞争条件,卡住可能是它的一种表现。

所以我建议采用更简单的设计,特别是没有用于发送消息的线程:

private ArrayList<byte[]> messageQueue = new ArrayList<byte[]>();
private boolean isSending = false;

void sendMessage(byte[] message) 
    synchronized (this) 
        if (isSending) 
            messageQueue.add(message);
            return;
        
        isSending = true;
    
    customCharacteristic.setValue(message);
    bluetoothGatt.writeCharacteristic(customCharacteristic);


public void onCharacteristicWrite (BluetoothGatt gatt, 
                BluetoothGattCharacteristic characteristic, 
                int status) 
    byte[] message;
    synchronized (this) 
        if (messageQueue.size() == 0) 
            isSending = false;
            return;
        
        message = messageQueue.remove(0);
    
    customCharacteristic.setValue(message);
    bluetoothGatt.writeCharacteristic(customCharacteristic); 

此解决方案中的假设是writeCharacteristic 不会阻塞并且速度很快。这是一个安全的假设,因为该方法在设计上是异步的:它有一个回调,将在操作完成时调用。

所以回调onCharacteristicWrite 用于发送缓冲区中的下一条消息。因此,对线程的需求消失了——相关的复杂性也消失了。

由于回调是从后台线程调用的,因此仍然涉及多个线程。因此,对共享数据的访问是同步的。

【讨论】:

会不会是 WRITE_TYPE_NO_RESPONSE 的问题? 是的,使用 WRITE_TYPE_DEFAULT 特性不会挂起,但是在 onCharacteristicWrite 回调之间需要很长时间才能按时发送数据。时间是必不可少的。 @Codo 例如,使用 WRITE_TYPE_NO_RESPONSE 在写入后放置 Thread.sleep(10) 会有所帮助,但它比预期的要慢。 这些 cmets 是引用我的代码还是仍然引用您的原始代码?如果他们引用我的代码:你遇到了什么问题? 您的代码简化了事情,但是在使用您的代码发送带有 WRITE_TYPE_NO_RESPONSE 的消息后,我仍然遇到同样的锁定。在这种写入模式下,睡眠有助于通过所有这些,但仍会偶尔锁定。如果没有 WRITE_TYPE_NO_RESPONSE,我不会收到错误消息,但是发送消息之间的时间有点长。

以上是关于Android BLE write Characteristic 锁定 onCharacteristicWrite/onCharacteristicChange的主要内容,如果未能解决你的问题,请参考以下文章

是否可以使用支持 BLE 的 Android/iPhone 作为 BLE 信标?

android BLE Peripheral 手机模拟设备发出BLE广播 BluetoothLeAdvertiser

android BLE Peripheral 手机模拟设备发出BLE广播 BluetoothLeAdvertiser

Android BLE 库分享

Android 10 BLE 连接问题

Android BLE 通知