一、问题描述
最近在对接蓝牙水表的代码编写中发现了两个问题:
(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() {
@Override
public 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;
}