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

时间:2014-10-09 00:54:34

标签: java android multithreading 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();
    }
}

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

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

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

2 个答案:

答案 0 :(得分: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并重复
  12. 第一个错误:

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

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

    doRead()执行时,它将进入休眠状态,从而阻止执行。这可以防止回调方法完成,并显然可以解决整个BLE通信系统。

    第二个错误:

    一旦我意识到上述问题,我将方法改为:

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

    据我所知,上述版本的方法应该可行。我正在创建一个运行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();
    }
    

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

答案 1 :(得分:2)

我通过添加以下代码解决了这个问题

        @Override
    public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
        new Thread(new Runnable() {
            @Override
            public void run() {
                gatt.readCharacteristic(characteristic);
            }
        }).start();
    }