首先,上框架的连接:https://github.com/Jasonchenlijian/FastBle
转: 作者文章: Android BLE开发详解和FastBle源码解析:https://www.jianshu.com/p/795bb0a08beb
1. BLE 扫描不到设备,也未报错。查看Log显示警告:
Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results
原因: 定位权限未允许。打开系统应用,找到对应的app,把定位权限打开,除了定位权限之后,部分手机需要打开定位开关。
待解决:如何在用户选择:不再询问,之后弹窗打开权限 ,或者打开定位开关。
2.Fastble 写特征值失败:gatt writeCharacteristic fail??
2021-0207-132643:error::startWrite() : 写失败==[0x5a, 0x00, 0x0b, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0xf1, 0x3b]BleException { code=102, description='gatt writeCharacteristic fail'}
出现 此问题之后,会一直写有问题? 要如何处理此问题?
看了一下出错的地方:蓝牙Gatt writeCharacteristic 返回false(bluetoothGatt writeCharacteristic returns false)
BluetoothGatt.class
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {}.
看百度有人说读写间增加1s的延时再操作。
解决:目前解决办法,是如写失败会增加延时来再写一次。 然后按设备端要求先打开Notify对应的特征值再开始写。
3. 打开Notity, 操作写Write失败。出错类:BleConnector.class, Notify Setting 方法报错。
mNotifier,打开failed,BleException { code=102, description='gatt writeDescriptor fail'}
2021-0220-111638:error::startWrite() : 写失败==[0x5a, 0x00, 0x05, 0x00, 0x01, 0x1d, 0x01, 0x00, 0x5d, 0xae]BleException { code=102, description='gatt writeCharacteristic fail'}
原因:未知,1/30概率出现,不知道是否与设备有关。
/**
* notify setting
*/
private boolean setCharacteristicNotification(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
boolean useCharacteristicDescriptor,
boolean enable,
BleNotifyCallback bleNotifyCallback) {
if (gatt == null || characteristic == null) {
notifyMsgInit();
if (bleNotifyCallback != null)
bleNotifyCallback.onNotifyFailure(new OtherException("gatt or characteristic equal null"));
return false;
}
boolean success1 = gatt.setCharacteristicNotification(characteristic, enable);
if (!success1) {
notifyMsgInit();
if (bleNotifyCallback != null)
bleNotifyCallback.onNotifyFailure(new OtherException("gatt setCharacteristicNotification fail"));
return false;
}
BluetoothGattDescriptor descriptor;
if (useCharacteristicDescriptor) {
descriptor = characteristic.getDescriptor(characteristic.getUuid());
} else {
descriptor = characteristic.getDescriptor(formUUID(UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR));
}
if (descriptor == null) {
notifyMsgInit();
if (bleNotifyCallback != null)
bleNotifyCallback.onNotifyFailure(new OtherException("descriptor equals null"));
return false;
} else {
descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE :
BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
boolean success2 = gatt.writeDescriptor(descriptor);
if (!success2) {
notifyMsgInit();
if (bleNotifyCallback != null)
bleNotifyCallback.onNotifyFailure(new OtherException("gatt writeDescriptor fail"));
}
return success2;
}
}
解决方法1: 收到失败监听方法,增加延时再次调用 。
@Override
public void onNotifyFailure(final BleException exception) {
LogUtils.e("mNotifier,打开failed" + exception.toString());
if (null != bleDevice) {
bleDevice.setNotify(false);
startNotify(bleDevcie);
}
}
}
以上收到失败之后不要立即再尝试打开Notify; 我增加一个2s的延时, startNotify(bleDevcie);。
@Override
public void onNotifyFailure(final BleException exception) {
LogUtils.e("mNotifier,打开failed" + exception.toString());
if (null != bleDevice) {
if (connectorHandler != null) {
connectorHandler.postDelayed(new Runnable() {
@Override
public void run() {
bleDevice.setNotify(false);
startNotify(bleDevcie);
}
}, 2000);
}
}
}
其他情况 :设备ble导致Notify打开失败,需要先打开特征值读写功能。
目前参考上面的方法:一般会出现2次Notify打开失败, 但是最终还是能打开成功。
分析:以下2个ble处理都是用hanler切换回主线程操作。
BleBluetooth.class 使用了MainHandler mainHandler = new MainHandler(Looper.getMainLooper());
BleConnector.class 使用mHandler = new Handler(Looper.getMainLooper())
oom解决:优化方法,SplitWrite.class 频繁创建BleConnector.class改成只创建一个。
// BleConnector.class 增加释放方法
public void release() {
try {
if (null != mBHandler) {
mBHandler.removeCallbacksAndMessages(null);
}
mBHandler = null;
} catch (Exception e) {
e.printStackTrace();
}
}
// ---SpliWriter
public class SplitWriter {
private HandlerThread mSHandlerThread;
private SHandler mSHandler;
private BleBluetooth mBleBluetooth;
private String mUuid_service;
private String mUuid_write;
private byte[] mData;
private int mCount;
private boolean mSendNextWhenLastSuccess;
private long mIntervalBetweenTwoPackage;
private BleWriteCallback mCallback;
private Queue<byte[]> mDataQueue;
private int mTotalNum;
private BleWriteCallback bleWriteCallback;
private BleConnector bleConnector;
public SplitWriter() {
mSHandlerThread = new HandlerThread("splitWriter");
mSHandlerThread.start();
mSHandler = new SHandler(this, mSHandlerThread.getLooper());
}
public void splitWrite(BleBluetooth bleBluetooth,
String uuid_service,
String uuid_write,
byte[] data,
boolean sendNextWhenLastSuccess,
long intervalBetweenTwoPackage,
BleWriteCallback callback) {
mBleBluetooth = bleBluetooth;
mUuid_service = uuid_service;
mUuid_write = uuid_write;
mData = data;
mSendNextWhenLastSuccess = sendNextWhenLastSuccess;
mIntervalBetweenTwoPackage = intervalBetweenTwoPackage; //每个包发送的时间间隔
mCount = BleManager.getInstance().getSplitWriteNum();// 按钮 20个byte来拆开
mCallback = callback;
splitWrite();
}
private void splitWrite() {
if (mData == null) {
throw new IllegalArgumentException("data is Null!");
}
if (mCount < 1) {
throw new IllegalArgumentException("split count should higher than 0!");
}
mDataQueue = splitByte(mData, mCount); // 按20个字节拆开放到队列
mTotalNum = mDataQueue.size();
write();
}
private void write() {
if (mDataQueue.peek() == null) { // Handler循环写,直接队列为空。
release();
return;
}
LogUtils.json("Write(),当前线程打印。");
byte[] data = mDataQueue.poll();
if (null == bleConnector) {
bleConnector = mBleBluetooth.newBleConnector().withUUIDString(mUuid_service, mUuid_write);
}
bleConnector.writeCharacteristic(data, getmCallback(), mUuid_write);
if (!mSendNextWhenLastSuccess) {
if (mSHandler == null) return;
mSHandler.sendEmptyMessageDelayed(BleMsg.MSG_SPLIT_WRITE_NEXT,
mIntervalBetweenTwoPackage);
}
}
private BleWriteCallback getmCallback() {
if (null == bleWriteCallback) {
bleWriteCallback = new BleWriteCallback() {
@Override
public void onWriteSuccess(int current, int total, byte[] justWrite) {
int position = mTotalNum - mDataQueue.size();
if (mCallback != null) {
mCallback.onWriteSuccess(position, mTotalNum, justWrite);
}
if (mSendNextWhenLastSuccess) {
LogUtils.json("SplitWriter 当前线程"); // BLE sub thread 1
if (mSHandler == null) return;
mSHandler.sendEmptyMessageDelayed(BleMsg.MSG_SPLIT_WRITE_NEXT,
mIntervalBetweenTwoPackage);//包与包写给手环要增加时间间隔
}
}
@Override
public void onWriteFailure(BleException exception) {
try {
if (mCallback != null) {
mCallback.onWriteFailure(new OtherException("exception occur while writing: " + exception.getDescription()));
}
if (mSendNextWhenLastSuccess) {
if (mSHandler == null) return;
mSHandler.sendEmptyMessageDelayed(BleMsg.MSG_SPLIT_WRITE_NEXT,
mIntervalBetweenTwoPackage);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
return bleWriteCallback;
}
public void release() {
if (null != mSHandlerThread) {
mSHandlerThread.quit();
}
if (null != mSHandler) {
mSHandler.removeCallbacksAndMessages(null);
}
if (null != bleConnector) {
bleConnector.release();
}
bleConnector = null;
mSHandlerThread = null;
mSHandler = null;
}
/**
* 将数据拆成20byte一包,放到队列
*
* @param data
* @param count
* @return
*/
private static Queue<byte[]> splitByte(byte[] data, int count) {
if (count > 20) {
LogUtils.w("Be careful: split count beyond 20! Ensure MTU higher than 23!");
}
Queue<byte[]> byteQueue = new LinkedList<>();
int pkgCount;
if (data.length % count == 0) {//拆开之后,包个数
pkgCount = data.length / count;
} else {
pkgCount = Math.round(data.length / count + 1);
}
byte[] dataPkg;
int j;
if (pkgCount > 0) {
for (int i = 0; i < pkgCount; i++) {
if (pkgCount == 1 || i == pkgCount - 1) {
j = data.length % count == 0 ? count : data.length % count;
System.arraycopy(data, i * count, dataPkg = new byte[j], 0, j);
} else {
System.arraycopy(data, i * count, dataPkg = new byte[count], 0, count);
}
byteQueue.offer(dataPkg);
}
}
return byteQueue;
}
private static class SHandler extends Handler {
private WeakReference<SplitWriter> reference;
public SHandler(SplitWriter splitWriter, Looper looper) {
super(looper);
reference = new WeakReference<>(splitWriter);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
SplitWriter splitWriter = reference.get();
if (null == splitWriter) return;
if (msg.what == BleMsg.MSG_SPLIT_WRITE_NEXT) {
splitWriter.write();
}
}
}
}