一、问题描述
最近在对接蓝牙水表的代码编写中发现了两个问题:
(1)通过app下发指令无响应,但通过蓝牙调试app下发相同指令有响应;
(2)app接收响应信息不完全。
经查,发现问题出于安卓蓝牙底层,write方法当单次发送、接收信息长度大于20字节时,需要手动分段进行发送与接收,若采用通常的接收与发送方法,则设备只能接收与发送第一段20字节的信息,从而导致以上问题的出现。
二、解决方式
通过简单的分段发送与分段接收拼接即可实现。
三、代码展示
1 分段接收方法
⚠️ 注意:
(1)该部分代码通过比较返回信息中的包长信息与当前接收到的总包长,判断是否完成接收,应根据实际需要修改;
(2)蓝牙初始化、销毁的时候执行this.packageNum = 0;
重置包数;
(3)判断到完成接收后需在分段方法中执行this.packageNum = 0;
重置包数,而不是在下发方法中执行,否则可能造成包数错误,从而导致超时(开发环境未出现但生产环境会出现)。
private void subpackageReceive(byte[] bytes) {packageNum++;if (packageNum == 1) {packageHex = new StringBuilder();packageLength = bytes[1] & (0xff);}packageHex.append(DataUtils.hex2(bytes, 0, bytes.length - 1));if (packageHex.length() == 2 * packageLength) {this.packageNum = 0;parserData(DataUtils.hexStrToByteArray(packageHex.toString()));}}
2 分段发送方法
2.1 拆分方法
public static synchronized List<byte[]> subpackage(byte[] data) {List<byte[]> bytesList = new ArrayList<>();if (data.length <= 20) {bytesList.add(data);return bytesList;}int partNum = data.length / 20;int lastPartLen = 20;if (data.length % 20 != 0) {partNum += 1;lastPartLen = data.length % 20;}for (int n = 0; n < partNum; n++) {String dataPart = "";if (n == partNum - 1) {dataPart = DataUtils.hex2(data, n * 20, lastPartLen - 1);} else {dataPart = DataUtils.hex2(data, n * 20, 19);}bytesList.add(DataUtils.hexStrToByteArray(dataPart));}return bytesList;}
2.2 发送方法
⚠️ 注意:
循环执行write发送指令时需间隔一小段时间发送,通过线程休眠实现。
for (byte[] data : dataList) {if (dataList.size() > 1) {try {Thread.sleep(10);} catch (InterruptedException e) {}}mBluetoothClient.write(mac, UUID.fromString(serviceUUID), UUID.fromString(writeUUID), data, new BleWriteResponse() {@Overridepublic void onResponse(int code) {if (code == REQUEST_SUCCESS) {}}});}
3 工具类
3.1 字节数组转十六进制字符串
static public String hex2(byte[] buf, int off, int len) {StringBuilder sb = new StringBuilder(len * 2);for (int j = off; j <= off + len; j++) {sb.append(hexString.charAt((buf[j] & 0xf0) >> 4));sb.append(hexString.charAt((buf[j] & 0x0f) >> 0));}return sb.toString();}
3.2 十六进制字符串转字节数组
public static byte[] hexStrToByteArray(String hexStr) {if (hexStr == null) {return null;}if (hexStr.length() == 0) {return new byte[0];}byte[] byteArray = new byte[hexStr.length() / 2];for (int i = 0; i < byteArray.length; i++) {String subStr = hexStr.substring(2 * i, 2 * i + 2);byteArray[i] = ((byte) Integer.parseInt(subStr, 16));}return byteArray;}