java - 安卓蓝牙 : onCharacteristicRead() appears to be blocked by thread

标签 java android multithreading bluetooth-lowenergy android-bluetooth

我正在针对 BLE 设备实现一系列特征读取。因为 readCharacteristic() 是异步执行的,并且因为我们必须等到它完成才能发出另一个“读取”调用,所以我对 wait() 使用了锁,然后在 'onCharacteristicRead() I notify() 锁让事情再次进行。

当我在调用 readCharacteristic() 之后wait() 时,我从未收到对 onCharacteristicRead() 的调用。如果我不 wait(),那么我会调用 onCharacteristicRead() 并报告正确的值。

下面是似乎阻止回调到 onCharacteristicRead() 的相关代码:

private void doRead() {
    //....internal accounting stuff up here....
    characteristic = mGatt.getService(mCurrServiceUUID).getCharacteristic(mCurrCharacteristicUUID);
    isReading = mGatt.readCharacteristic(characteristic);

    showToast("Is reading in progress? " + isReading);

    showToast("On thread: " + Thread.currentThread().getName());

    // Wait for read to complete before continuing.
    while (isReading) {
        synchronized (readLock) {
            try {
                readLock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    showToast("onCharacteristicRead()");
    showToast("On thread: " + Thread.currentThread().getName());

    byte[] value = characteristic.getValue();
    StringBuilder sb = new StringBuilder();
    for (byte b : value) {
        sb.append(String.format("%02X", b));
    }

    showToast("Read characteristic value: " + sb.toString());

    synchronized (readLock) {
        isReading = false;
        readLock.notifyAll();
    }
}

如果我简单地删除上面的 while() 语句,我就成功地获得了读取回调。当然,这会阻止我等待进一步阅读,所以我不能不等待就继续前进。

鉴于 readCharacteristic() 是异步的,为什么调用线程的执行与实际执行读取的能力或调用回调的能力有任何关系?

为了让事情变得更加困惑,我展示了一个 toast,它在我调用 readCharacteristic() 以及调用 onCharacteristicRead() 时标识线程。这 2 个线程有不同的名称。我认为可能出于某种原因在调用线程上调用了回调,但事实并非如此。那么这里的线程是怎么回事?

最佳答案

这里的问题似乎是线程的一个模糊问题,在我原来的帖子中看不到它,因为我没有发布足够多的调用历史记录来查看它。我会解释我在这里发现的内容,以防影响到其他人。

导致我出现问题的完整通话记录是这样的:

  1. 开始乐扫
  2. 查找我关心的设备
  3. 连接到设备的 GATT 服务器(返回 GATT 客户端,我在其中为所有异步通信调用提供 BluetoothGattCallback)
  4. 告诉 GATT 客户端 discoverServices()
  5. 片刻之后,BLE 系统调用我的回调的 onServicesDiscovered()
  6. 现在我已准备好开始读取特征,因为服务数据已加载,因此这是我在原始帖子中调用 doRead() 方法的地方
  7. 告诉 GATT 客户端 readCharacteristic()
  8. 去 sleep 直到阅读完成 ---- 这是死锁发生的地方,但它应该:
  9. 片刻之后,BLE 系统调用我的回调的 onCharacteristicRead()
  10. 通知所有等待的线程
  11. 返回第 7 步并重复

第一个错误:

最初我的 onServicesDiscovered() 方法是这样的:

public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
    doRead();
}

doRead() 执行时,它将进入 hibernate 状态并因此阻止执行。这会阻止回调方法完成并明显破坏整个 BLE 通信系统。

第二个错误:

意识到上述问题后,我将方法更改为以下:

public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            doRead();
        }
    ).start();
}

据我所知,上述版本的方法应该有效。我正在创建一个运行 doRead() 的新线程,因此在 doRead() 中 hibernate 应该不会对 BLE 线程产生任何影响。 但确实如此!此更改没有影响。

------------ 编辑笔记------------

在发布这个之后,我真的无法解释为什么上面的匿名线程不起作用。所以我又试了一次,这次它成功了。不确定第一次出了什么问题,也许我忘了在线程上调用 start() 之类的......

------------结束编辑笔记------------

解决方案:

最后,一时兴起,我决定在我的类实例化时创建一个后台 HandlerThread(而不是在 onServicesDiscovered() 中启动一个匿名的 Thread )。该方法现在看起来像这样:

public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
    mBackgroundHandler.post(new Runnable() {
        @Override
        public void run() {
            doRead();
        }
    ).start();
}

上述版本的方法有效。在读取前一个特征时,对 doRead() 的调用成功地迭代了每个特征。

关于java - 安卓蓝牙 : onCharacteristicRead() appears to be blocked by thread,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26268713/

相关文章:

Java图像显示问题

java - 在Java中,如何找到自上次鼠标移动以来的时间?

android - 监控用户何时启用/禁用移动数据

c - 与线程共享变量的最佳方式

C# 线程内存使用

java - Java 中的字符串大十进制数

java - 在 JTabbedPane 中设置选项卡背景

android - ScrollView 不闪烁。缓慢滚动

java - 使用 spring for android 或使用 google io 中讨论的 Virgil Doobjanschi 模式使用 RESTful api

ios - 可取消后台任务 : NSOperation, GCD 或 NSThread?