简体   繁体   English

Android BLE write Characteristic locks up onCharacteristicWrite/onCharacteristicChange

[英]Android BLE write Characteristic locks up onCharacteristicWrite/onCharacteristicChange

I have a message thread for sending a buffer of messages.我有一个用于发送消息缓冲区的消息线程。 Each message is queued to be sent once onCharacteristicWrite is successful before the characteristic writes the next message.在特征写入下一条消息之前,每条消息都会排队等待onCharacteristicWrite成功发送。 The characeristic is also set to WRITE_TYPE_NO_RESPONSE , so the message buffer queue is pretty fast (0-7ms approximately) between characteristic write calls. characeristic 也设置为WRITE_TYPE_NO_RESPONSE ,因此消息缓冲区队列在特征写入调用之间非常快(大约 0-7 毫秒)。

The main issue: "jammed up" characteristic主要问题:“卡住”特性

For the most part this works great.在大多数情况下,这很有效。 The problem seems to arise when there's a large amount of messages (possibly happens with fewer message, but is more noticeable when sending lots of messages).当有大量消息时,似乎会出现问题(可能在消息较少时发生,但在发送大量消息时更明显)。 What happens is writeCharacteristic will get called, and the characteristic seems to lock up, since onCharacteristicChanged no longer reads any new data, and doesn't get to onCharacteristicWrite .发生的事情是writeCharacteristic将被调用,并且该特征似乎被锁定,因为onCharacteristicChanged不再读取任何新数据,并且不会到达onCharacteristicWrite

Other things I've noticed:我注意到的其他事情:

  1. Adding a sleep delay of 5-10ms after each characteristicWrite seems to help, but I don't understand why the Bluetooth GATT object would need a delay when onCharacteristicWrite returns successful.在每个characteristicWrite写入后添加 5-10 毫秒的睡眠延迟似乎有帮助,但我不明白为什么蓝牙 GATT object 在onCharacteristicWrite返回成功时需要延迟。

  2. Sometimes I'll get a callback in onConnectionStateChange with status 8, device out of range.有时我会在onConnectionStateChange中收到状态为 8 的回调,设备超出范围。 This doesn't always happen though.不过,这并不总是发生。

  3. Sometimes characteristicWrite returns false;有时, characteristicWrite返回 false; however, it can also return true before going into the "jammed up characteristic" state described above但是,它也可以在进入上述“阻塞特性”state 之前返回 true

Message Thread code:消息线程代码:

    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
                }
            }
        }
    });

Aside from busy waiting, which can block an entire CPU and quickly drain the battery, I can't see any synchronization.除了忙等待会阻塞整个 CPU 并迅速耗尽电池,我看不到任何同步。 There are shared data structures (likely stopMessageThread , characteristicWriteSuccessful and messageQueue ) and several threads accessing them.有共享的数据结构(可能是stopMessageThreadcharacteristicWriteSuccessfulmessageQueue )和几个访问它们的线程。 Without synchronization, race conditions will occur and the jam up could be a manifestation of it.如果没有同步,就会出现竞争条件,并且堵塞可能是它的一种表现。

So I propose to go with a simpler design, in particular without a thread for sending messages:所以我建议 go 设计更简单,特别是没有用于发送消息的线程:

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); 
}

The assumption in this solution is that writeCharacteristic does not block and is quick.此解决方案中的假设是writeCharacteristic不会阻塞并且速度很快。 That's a safe assumption as the method is asynchronous by design: it has a callback that will be called when the operation is complete.这是一个安全的假设,因为该方法在设计上是异步的:它有一个回调,将在操作完成时调用。

So the callback onCharacteristicWrite is used to send the next message in the buffer.所以回调onCharacteristicWrite用于发送缓冲区中的下一条消息。 Therefore, the need for the thread goes away – and so goes the associated complexity.因此,对线程的需求消失了——相关的复杂性也消失了。

There are still several threads involved as the callback is called from a background thread.由于回调是从后台线程调用的,因此仍然涉及多个线程。 Therefore, the access to the shared data is synchronized.因此,对共享数据的访问是同步的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM