Android BLE - 基于Fastble 框架的蓝牙开发 使用遇到 问题 OOM

3 篇文章 1 订阅

首先,上框架的连接: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打开失败, 但是最终还是能打开成功。

 

4. OutOfMemoryError BLE通信量大,阻塞主线程其他功能运行,造成Activity打开卡顿。  

 分析:以下2个ble处理都是用hanler切换回主线程操作。

  • BleBluetooth.class 使用了MainHandler mainHandler = new MainHandler(Looper.getMainLooper());
  • BleConnector.class 使用mHandler = new Handler(Looper.getMainLooper())
  • 解决:将以下2个handle切换HandlerThread 子线程处理类。目前大部分功能正常,但是有部分子线程与子线程通信不正常。待解决。
  • 切为HandlerThread之后,做OTA升级,需要大量通信,造成OOM问题。splitWriter.class 类每发一个大于20字节的包就会创造一个对象,对象里面包含着子线程。造成内存消耗。
  •   见下面报错:(待解决。
    •   2021-03-17 09:59:57.245 21190-30225/com.gtk.darwin W/com.gtk.darwin: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"
      2021-03-17 09:59:57.247 21190-30225/com.gtk.darwin E/AndroidRuntime: FATAL EXCEPTION: splitWriter
          Process: com.gtk.darwin, PID: 21190
          java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
              at java.lang.Thread.nativeCreate(Native Method)
              at java.lang.Thread.start(Thread.java:733)
              at com.gtkcep.blelib.bluetooth.BleConnector.<init>(BleConnector.java:47)
              at com.gtkcep.blelib.bluetooth.BleBluetooth.newBleConnector(BleBluetooth.java:61)
              at com.gtkcep.blelib.bluetooth.SplitWriter.write(SplitWriter.java:89)
              at com.gtkcep.blelib.bluetooth.SplitWriter.access$000(SplitWriter.java:20)
              at com.gtkcep.blelib.bluetooth.SplitWriter$1.handleMessage(SplitWriter.java:45)
              at android.os.Handler.dispatchMessage(Handler.java:106)
              at android.os.Looper.loop(Looper.java:193)
              at android.os.HandlerThread.run(HandlerThread.java:65)

 

oom解决:优化方法,SplitWrite.class 频繁创建BleConnector.class改成只创建一个。

  •  if (null == bleConnector) {
                bleConnector = mBleBluetooth.newBleConnector().withUUIDString(mUuid_service, mUuid_write);
            }
            bleConnector.writeCharacteristic(data, getmCallback(), mUuid_write);
  • // BleConnector.class 增加释放方法, 在SplitWrite.class发完所有的拆包之后,调用 。并且改原来框架handler, 不要用HandlerThread。
  • // 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();
                }
            }
        }
    
    }
    

     

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值