Android BLE writeCharacteristic偶尔返回false的处理方法

背景:

最近在负责做RFID的一个项目,PDA通过BLE和BLE模块通信,BLE模块透传CMD给到RFID模块,然后RFID模块回Response,通过BLE模块给到PDA。

做好一些SDK的接口自己做压力测试的时候,发现很高频率的情况(例如间隔40,50ms或更短执行一次)下调用

boolean status = mBluetoothGatt.writeCharacteristic(characteristic);
Log.e("potter","status:"+status);
或者
boolean status = mBluetoothGatt.readCharacteristic(characteristic);
Log.e("potter","status:"+status);

返回值有时候是false,蓝牙模块也没有收到数据。正常情况是返回值为true,蓝牙模块收到数据。

追踪源码:

BluetoothGatt.java类
private Boolean mDeviceBusy = false;
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
    …
    synchronized (mDeviceBusy) {
        if (mDeviceBusy) return false;
        mDeviceBusy = true;
    }
    …
    try {
        //这里才执行wirte操作
        mService.writeCharacteristic(mClientIf, device.getAddress(),
                characteristic.getInstanceId(), characteristic.getWriteType(),
                AUTHENTICATION_NONE, characteristic.getValue());
    } catch (RemoteException e) {
        Log.e(TAG, "", e);
        mDeviceBusy = false;
        return false;
    }
}
public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
    …
    synchronized (mDeviceBusy) {
        if (mDeviceBusy) return false;
        mDeviceBusy = true;
    }
    …
    try {
        //这里才执行read操作
        mService.readCharacteristic(mClientIf, device.getAddress(),
            characteristic.getInstanceId(), AUTHENTICATION_NONE);
    } catch (RemoteException e) {
        Log.e(TAG, "", e);
        mDeviceBusy = false;
        return false;
    }
}
private final IBluetoothGattCallback mBluetoothGattCallback =
        new IBluetoothGattCallback.Stub() {
    ...
    public void onCharacteristicRead(String address, int status, int handle,
            byte[] value) {
        ...
        synchronized (mDeviceBusy) {
            mDeviceBusy = false;
        }
        ...
        runOrQueueCallback(new Runnable() {
            @Override
            public void run() {
	            //对应我们自己code里面的回调
                final BluetoothGattCallback callback = mCallback;
                if (callback != null) {
                    if (status == 0) characteristic.setValue(value);
                    callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
                            status);
                }
        }
    });
    public void onCharacteristicWrite(String address, int status, int handle) {
        ...
        synchronized (mDeviceBusy) {
            mDeviceBusy = false;
        }
        ...
        runOrQueueCallback(new Runnable() {
            @Override
            public void run() {
	            //对应我们自己code里面的回调
                final BluetoothGattCallback callback = mCallback;
                if (callback != null) {
                    callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
                            status);
                }
        }
    });
    ...

}

原因

看了源码就一目了然了,当我们高频率的writeCharacteristic或readCharacteristic的时候,返回false的时候就是DeviceBusy的时候。原因是writeCharacteristic后,对应的onCharacteristicWrite还没执行到,第二次writeCharacteristic就来了,此时mDeviceBusy的值是true,就直接返回false了。

解决方法:

1.设置两条CMD间的间隔

实测100ms还是比较稳定的,对外的接口设计成,例如每发送一条CMD后,Thread.sleep(100)。注意提示客户别多线程调用接口就ok了。

但是这样设计其实是有风险的,可能不同的BLE模块的差异,现场环境的差异,或者PDA的差异导致实际比较可靠的间隔大于100ms,或者说本来80ms就够了的,但是却等了100ms,从代码的角度来考虑不够优雅。

2.根据mDeviceBusy这个值来做文章

我们设置一个超时,每次writeCharacteristic前,超时范围内不断的读mDeviceBusy,当Device不是busy的时候,跳出循环,去执行writeCharacteristic。

比较蛋疼是mDeviceBusy是private的,外部拿不到。

BluetoothGatt.java内也没有定义类似

public boolean isDeviceBusy(){

    return mDeviceBusy;

}

的方法。

所以我们用反射来调用,完整代码如下:

private static long HONEY_CMD_TIMEOUT = 2000;
private boolean isDeviceBusy(){
   boolean state = false;
   try {
      state = (boolean)readField(mBluetoothGatt,"mDeviceBusy");
      Log.e("potter123","isDeviceBusy:"+state);
   } catch (IllegalAccessException e) {
      e.printStackTrace();
   } catch (NoSuchFieldException e) {
      e.printStackTrace();
   }
   return state;
}
public  Object readField(Object object, String name) throws IllegalAccessException, NoSuchFieldException {
   Field field = object.getClass().getDeclaredField(name);
   field.setAccessible(true);
   return field.get(object);
}
public void writeCharacteristic(byte[] value,UUID serivceUUID,UUID characterUUID){
   if (mBluetoothGatt == null) {
      return;
   }
   BluetoothGattService service = null;
   long enterTime = System.currentTimeMillis();
   while ((System.currentTimeMillis() - enterTime) < HONEY_CMD_TIMEOUT) {
      if(isDeviceBusy()){
         try {
            Thread.sleep(10);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }else {
         break;
      }
   }
   try {
      service = mBluetoothGatt.getService(serivceUUID);
      BluetoothGattCharacteristic characteristic = service.getCharacteristic(characterUUID);
      characteristic.setValue(value);
      boolean status = mBluetoothGatt.writeCharacteristic(characteristic);
      Log.e("potter123","status:"+status);
   } catch (Exception e) {
      e.printStackTrace();
   }
}

实测,即使间隔很短的调用我们给的接口。每次status都是true了。

3.自己参考mDeviceBusy的处理逻辑封装

参考mDeviceBusy去回调的onCharacteristicRead,onCharacteristicWrite定义自己的逻辑。

因为一般来说,要有比较严谨的设计,mDeviceBusy这个状态的控制是不够的,最好能做到每一条cmd的下发(writeCharacteristic),和response(notify后的onCharacteristicChanged)能做到一一对应,比如HEAD,LENGTH,CMD,CRC等标志位的处理逻辑等,但在这里就不一一赘述了。

总结:

用mDeviceBusy来做判断还是比较巧妙的,之前在网上也没有找到比较合适的solution。这说明求人不如求己,自己看源码还是有收获的!

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Android平台上,使用BLE(低功耗蓝牙)进行设备通信需要编写源代码。下面是一个简单的示例,展示了如何启用BLE功能、搜索和连接设备、发送和接收数据。 首先,需要确保应用程序在AndroidManifest.xml文件中申请必要的权限和特性。 ```xml <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> ``` 接下来,在应用程序的MainActivity中创建一个BluetoothAdapter对象,并启用BLE功能。 ```java BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) { // 设备不支持蓝牙蓝牙未启用 // 进行相关处理 } ``` 然后,需要创建一个BluetoothLeScanner对象来搜索BLE设备。可以通过扫描结果回调来获取设备信息。 ```java BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); bluetoothLeScanner.startScan(new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { // 获取设备信息 BluetoothDevice device = result.getDevice(); // 进行相关处理,比如连接设备 } }); ``` 接下来,可以通过设备名称或MAC地址来连接设备,并与之进行数据通信。 ```java BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress); BluetoothGatt gatt = device.connectGatt(this, false, new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { // 设备已连接 // 进行相关处理,比如发现服务 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // 设备已断开连接 // 进行相关处理 } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { // 服务已发现 // 进行相关处理,比如获取特征值 } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { // 特征值已读取 // 进行相关处理 } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { // 特征值已写入 // 进行相关处理 } }); ``` 最后,可以使用BluetoothGatt对象向设备发送数据或者接收设备发送的数据。 ```java BluetoothGattCharacteristic characteristic = gatt.getService(serviceUuid).getCharacteristic(characteristicUuid); characteristic.setValue(data); gatt.writeCharacteristic(characteristic); ``` 这只是一个简单的示例,实际的BLE设备通信可能会涉及更多的操作和数据处理。使用上述代码作为起点,可以根据实际需求进行扩展和改进。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值