Android BLE写入特征锁定onCharacteristicWrite / onCharacteristicChange

时间:2019-10-11 00:59:48

标签: java android bluetooth-lowenergy bluetooth-gatt

我有一个消息线程,用于发送消息缓冲区。一旦onCharacteristicWrite成功发送一次,每个消息就排队等待发送,直到特征写入下一条消息。特征也设置为WRITE_TYPE_NO_RESPONSE,因此两次特征写入之间的消息缓冲区队列非常快(大约0-7ms)。

主要问题:“卡住”特征

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

我注意到的其他事情:

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

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

  3. 有时characteristicWrite返回false;但是,它也可以在进入上述“堵塞特征”状态之前返回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
                }
            }
        }
    });

1 个答案:

答案 0 :(得分:1)

除了忙碌的等待(它可能阻塞整个CPU并迅速耗尽电池)之外,我看不到任何同步。有共享的数据结构(可能是stopMessageThreadcharacteristicWriteSuccessfulmessageQueue),还有几个访问它们的线程。如果没有同步,则会出现竞争条件,并且 jam up 可能是这种情况的体现。

因此,我建议采用更简单的设计,尤其是没有用于发送消息的线程:

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;
    }
    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);
    }
    bluetoothGatt.writeCharacteristic(customCharacteristic); 
}

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

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

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