当我等待()调用readCharacteristic()后,我从未得到onCharacteristicRead()的调用.如果我不等待(),那么我会调用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()被调用时显示一个标识线程的吐司.这两个线程具有不同的名称.我以为可能是因为某些原因在调用线程上调用了回调,但是似乎并非如此.那么这里的线程是怎么回事?
解决方法
导致我问题的完整通话历史记录如下:
>启动Le Scan
>查找我关心的设备
>连接到设备的GATT服务器(返回一个GATT客户端,并且为所有异步通信呼叫提供一个BluetoothGattCallback)
>告诉GATT客户端发现服务()
>稍后,BLE系统调用我的回调函数onServicesDiscovered()
>现在我准备好开始阅读特征,因为服务数据被加载,所以这是我在原始帖子中调用doRead()方法的地方
>告诉GATT客户端读取特征()
>去睡觉,直到阅读完成
—-这是死锁发生的地方,但它应该是:
稍后,BLE系统调用我的callbacck的onCharacteristicRead()
>通知所有等待线程
>返回步骤7并重复
第一个错误:
本来我的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()
———结束编辑注————
解决方案:
最后,当我的类被实例化(而不是在onServicesDiscovered())上创建一个匿名Thread时,我决定创建一个后台HandlerThread.现在的方法如下所示:
public void onServicesDiscovered(final BluetoothGatt gatt,int status) { mBackgroundHandler.post(new Runnable() { @Override public void run() { doRead(); } ).start(); }