Android BLE:onCharacteristicRead()似乎被线程阻止

Flu*_*ery 11 java multithreading android bluetooth-lowenergy android-bluetooth

我正在针对BLE设备实现一系列特征性读取.因为readCharacteristic()异步执行,并且因为我们必须等到它在发出另一个"读"调用之前完成,所以我使用了一个锁wait(),然后在'onCharacteristicRead()notify()的锁中再次进行操作.

当我wait()打电话后readCharacteristic(),我从来没有打过电话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();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我只是删除while()上面的语句,我成功地获得了读回调.当然,这阻止了我等待进一步阅读,所以我不能不等待前进.

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

为了让事情更加混乱,我展示了一个toast,它在我调用时标识线程readCharacteristic(),以及何时onCharacteristicRead()被调用.这两个线程有​​不同的名称.我认为可能由于某种原因在调用线程上调用了回调,但事实并非如此.那么这里的线程是怎么回事?

Flu*_*ery 18

这里的问题似乎是线程的一个模糊问题,而且在我的原帖中看不到,因为我没有发布足够的通话记录来查看它.我将解释我在这里找到的内容,以防它影响到其他人.

导致我的问题的完整通话记录如下:

  1. 开始Le Scan
  2. 找到我关心的设备
  3. 连接到设备的GATT服务器(返回GATT客户端,我为所有异步通信呼叫提供BluetoothGattCallback)
  4. 告诉关贸总协定客户 discoverServices()
  5. 片刻之后,BLE系统调用我的回调函数 onServicesDiscovered()
  6. 现在我已经准备好开始读取特性,因为服务数据被加载了,所以这是我doRead()在原始帖子中调用该方法的地方
  7. 告诉关贸总协定客户 readCharacteristic()
  8. 进入睡眠直到读取完成----这是发生死锁的地方,但它应该:
  9. 片刻之后,BLE系统调用了我的callbacck onCharacteristicRead()
  10. 通知所有等待的线程
  11. 回到第7步并重复一遍

第一个错误:

最初我的onServicesDiscovered()方法看起来像这样:

public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
    doRead();
}
Run Code Online (Sandbox Code Playgroud)

doRead()执行时,它正在睡觉,因此阻断执行.这可以防止回调方法完成并显然可以解决整个BLE通信系统问题.

第二个错误:

一旦我意识到上述问题,我将方法更改为以下内容:

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

据我所知,上述版本的方法应该可行.我正在创建一个要运行的新线程doRead(),所以睡觉doRead()不应该对BLE线程产生任何影响. 但确实如此! 这种变化没有影响.

-----------编辑说明--------------

发布之后,我真的无法理解为什么上面的匿名线程不起作用.所以我再试一次,这次它确实奏效了.不知道第一次出了什么问题,也许我忘了打电话start()给线程或其他东西......

---------结束编辑注意------------

解决方案:

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

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

上述版本的方法有效.doRead()读取成功遍历每个特征的调用.