简体   繁体   English

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

[英]Android BLE: onCharacteristicRead() appears to be blocked by thread

I'm implementing a series of characteristic reads against a BLE device. 我正在针对BLE设备实现一系列特征读取。 Because readCharacteristic() executes asynchronously, and because we have to wait until it completes before issuing another "read" call, I used a lock to wait() and then in 'onCharacteristicRead() I notify() the lock to get things going again. 因为readCharacteristic()异步执行的,并且由于我们必须等到它完成才发出另一个“读取”调用,所以我使用了锁来wait() ,然后在'onCharacteristicRead()notify()锁以使事情再次发生。 。

When I wait() after calling readCharacteristic() , I never get a call to onCharacteristicRead() . 当我在调用readCharacteristic() wait()之后wait() ,我再也没有调用过onCharacteristicRead() If I don't wait() , then I do get a call to onCharacteristicRead() and the correct value is reported. 如果我没有wait() ,那么我确实会调用onCharacteristicRead()并报告正确的值。

Here is the relevant code that seems to block the callback to 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();
    }
}

If I simply remove the while() statement above, I successfully get the read callback. 如果仅删除上面的while()语句,就可以成功获取read回调。 Of course, that prevents me from waiting to do further reads, so I can't move forward without waiting. 当然,这使我无法等待进一步的阅读,所以我不能不等待而前进。

Given that the readCharacteristic() is asynchronous, why would execution of the calling thread have anything to do with the ability to actually do the read, or the ability to call the callback? 鉴于readCharacteristic()是异步的,为什么调用线程的执行与实际执行读取的功能或调用回调的功能有关?

To make things more confusing, I show a toast which identifies the thread when I call readCharacteristic() , as well as when onCharacteristicRead() is invoked. 为了使事情更加混乱,我在调用readCharacteristic()以及调用onCharacteristicRead()时展示了一个标识线程的吐司。 These 2 threads have different names. 这两个线程具有不同的名称。 I thought that maybe the callback was being invoked on the calling thread for some reason, but that doesn't appear to be the case. 我认为也许是由于某种原因在调用线程上调用了回调,但事实并非如此。 So what is going on here with the threading? 那么线程在这里发生了什么?

The problem here appears to be an obscure issue with threading and it can't be seen in my original post because I didn't post enough of the call history to see it. 这里的问题似乎是线程方面的一个模糊问题,在我的原始帖子中看不到,因为我没有张贴足够多的呼叫历史记录来查看它。 I will explain what I found here in case it effects someone else. 我将在这里解释我发现的内容,以防其他人受到影响。

The full call history leading to my problem went something like this: 导致我的问题的完整通话记录如下:

  1. Start Le Scan 开始Le Scan
  2. Find device I care about 查找我关心的设备
  3. Connect to the device's GATT server (which returns a GATT client, and where I provide a BluetoothGattCallback for all the async communication calls) 连接到设备的GATT服务器(该服务器返回GATT客户端,在这里我为所有异步通信调用提供BluetoothGattCallback)
  4. Tell the GATT client to discoverServices() 告诉GATT客户端discoverServices()
  5. A moment later, the BLE system invokes my callback's onServicesDiscovered() 片刻之后,BLE系统调用了我的回调的onServicesDiscovered()
  6. Now I'm ready to start reading characteristics because the service data is loaded, so this is where I invoke that doRead() method in my original post 现在,因为加载了服务数据,所以我准备开始读取特征,因此这就是我在原始文章中调用doRead()方法的地方
  7. Tell the GATT client to readCharacteristic() 告诉GATT客户端readCharacteristic()
  8. Go to sleep until the read is done ---- This is where deadlock occurs, but its supposed to: 进入睡眠状态直到读取完成----这是发生死锁的地方,但是它应该:
  9. A moment later, the BLE system invokes my callbacck's onCharacteristicRead() 片刻之后,BLE系统调用了我的callbacck的onCharacteristicRead()
  10. Notify all waiting threads 通知所有等待线程
  11. Go back to step 7 and repeat 返回步骤7并重复

First Mistake: 第一个错误:

Originally my onServicesDiscovered() method looked like this: 最初,我的onServicesDiscovered()方法如下所示:

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

When doRead() executes, it is going to sleep and therefore block execution. doRead()执行时,它将进入睡眠状态,因此阻止了执行。 This prevents the callback method from finishing and apparently gunks up the entire BLE communicaiton system. 这阻止了回调方法的完成,并且显然使整个BLE通信系统陷入困境。

Second Mistake: 第二个错误:

Once I realized the above issue, I changed the method to the following: 一旦意识到上述问题,便将方法更改为以下内容:

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

As far as I can tell, the above version of the method should work. 据我所知,该方法的上述版本应该可以工作。 I'm creating a new thread on which to run doRead() , so sleeping in doRead() should not have any impact on the BLE thread. 我正在创建一个在其上运行doRead()的新线程,因此在doRead()休眠不应对BLE线程产生任何影响。 But it does! 但是可以! This change had no impact. 此更改没有影响。

----------- Edit Note -------------- -----------编辑便笺--------------

After posting this, I really couldn't rationalize why the above anonymous thread wouldn't work. 发布此内容后,我真的无法合理解释为什么上述匿名线程不起作用。 So I tried it again, and this time it did work . 所以我再次尝试了,这次它确实起作用了 Not sure what went wrong the first time, maybe I forgot to call start() on the thread or something... 不知道第一次出了什么问题,也许我忘记了在线程上调用start()之类的方法。

--------- End Edit Note ------------ ---------结束编辑说明------------

The Solution: 解决方案:

Finally, on a whim, I decided to create a background HandlerThread when my class gets instantiated (instead of spinning up an anonymous Thread in onServicesDiscovered() ). 最终,一时兴起,我决定在实例化我的类时创建一个后台HandlerThread (而不是在onServicesDiscovered()中旋转匿名Thread )。 The method now looks like this: 该方法现在如下所示:

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

The above version of the method works. 上面版本的方法有效。 The call to doRead() successfully iterates over each characteristic as the previous one is read. 读取前一个特性时,对doRead()的调用成功地遍历了每个特性。

I have solved this problem by adding the following code 我已经通过添加以下代码解决了这个问题

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

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

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