BlueTooth BLE 开发

本文介绍了Android蓝牙低功耗(BLE)开发中遇到的问题及其解决办法,包括扫描不到设备、连接失败、服务和特征值发现、连接断开等问题,同时强调了服务(Service)和特征值(Characteristic)在BLE中的作用,并提供了开发中的权限管理建议。
摘要由CSDN通过智能技术生成

蓝牙BLE是在Android4.3系统及以上引入的,Android BLE 使用的蓝牙协议是 GATT 协议。

BLE坑

Q:为什么扫描不到手机蓝牙?(排查了好久……)

A:因为手机和电脑普通方式广播的不是ble蓝牙,而手环是ble蓝牙。因此手机只能当ble的主机用。

Q:扫描不到设备

先确定下列几项是否满足:
1、蓝牙是否打开
2、蓝牙相关权限是否授权(6.0以上位置权限)
3、7.0以上手机很多需要手动打开GPS

Q:有时候刚开始扫描还正常,过段时间扫描不到设备?

原因: 出现这个问题的很多是Android7.0以上手机,为什么呢?
因为Google为了防止Android7中的BLE扫描滥用,从而做了一些限制,即不要在30s内对蓝牙扫描 重复开启-关闭超过5次。
建议:
设置扫描周期>6s, 用户点击扫描后不要重复进行扫描,可以做一个是否正在扫描的标志位,如果 正在扫描就不做重复扫描动作了。

Q:为什么有些手机退到后台扫描不到设备?

Android8.0以上退到后台息屏后,为了保证省电等原因,如果不设置ScanFilters的话是默认扫不到设备的,所以解决办法就是设置如下:
mScannerSetting = new ScanSettings.Builder()
//退到后台时设置扫描模式为低功耗
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build();
mFilters.add(new ScanFilter.Builder()
//过滤扫描蓝牙设备的主服务
.setServiceUuid(ParcelUuid.fromString(“0000ffff-0000-1000-8000-00805f9bfffb”))
.build());
mScanner.startScan(mFilters, mScannerSetting, mScannerCallback);

Q:为什么同时多个设备连接时经常连接不成功?

可能原因:
在使用 BluetoothDevice.connectGatt() 或者 BluetoothGatt.connect() 等建立BluetoothGatt 连接的时候,在任何时刻都只能最多一个设备在尝试建立连接。如果同时对多个蓝牙设备发起建立 Gatt 连接请求。如果前面的设备连接失败了,后面的设备请求会被永远阻塞住,不会有任何连接回调。
建议:
如果要对多个设备发起连接请求,最好建立一个请求队列,前一个设备请求建立连接,后面请求在队列中等待上调用BluetoothGatt.disconnect()/close()来释放建立连接请求,然后处理下一个设备连接请求。

Q:为什么有时候连接成功了,但是发现不了服务及特征值,进而影响数据的接收和发送。

连接成功后,会进行BluetoothGatt.discoverServices()去发现服务,进而设置特征值等,因为该方法是在主线程中执行的,所以为了连接过程的可靠性,建议不要在该过程中,在主线程中不要处理太多的操作(尤其是频 繁绘制操作)。

Q:为什么连接成功后,过不一会又断开了?

这个问题其实并不主要是客户端的问题,所以不要一味的在代码中找问题了,建议与硬件沟通,让其进行优化,如可以调整设备的连接参数(ConnectionInterval(连接间隔)、SlaveLatency(从设备延迟或者从设备时 延)、SupervisionTimeout(超时时间或者监控超时)),这三个参数是低功耗蓝牙中十分重要的连接参数,一起决定了BLE的功耗,一般硬件设备会在APP连接成功时主动去更新一下这三个参数,以保证不同手机的差异性得到一致,但是APP端是没办法控制这三个参数的。

Q:最大连接数问题

标准答案是7个,为什么是7个,这个我们可以从如下图所示的底层蓝牙源码中找到依据。

https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/include/bt_target.h#1428

stackoverflow问答社区
在这里插入图片描述
Q:连接api的使用问题

发起蓝牙Gatt连接 BluetoothDevice.connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback),这里有一个参数autoConnect,如果为 true的话,系统就会发起一个后台连接,等到系统发现了一个设备,就会自动连上,通常这个过程是非常慢的。为false 的话,就会直接连接,通常会比较快。同样,BluetoothGatt.connect()只能发起一个后台连接,不是直接连接,所以连接时设置autoConnect参数设置为false,如果想实现重连功能的话,自己去手动实现吧,实在不想手动写,那就用Android-BLE库吧,你想要的基本都有。

Q:为什么连接总是报133、19之类的非0异常

原因:可能由于首次连接蓝牙后没有释放掉gatt资源导致的蓝牙协议栈异常,从而出现133或257 19等值不为0:由于协议栈,连接建立失败。

建议:在onConnectionStateChange()回调中判断,若state非0(连接断开),调用gatt.close(),手动释放掉gatt相关资源。

Q:为什么连接成功了,发送数据总是失败?

原因:
1、首先确定主服务是否正确,再看设置的读、写特征值是否正确
2、因为BLE发现服务和设置特征、通知等是需要耗时的,所以你并不能连接成功后立马发送数据,可以等到在onDescriptorWrite()回调时,或者手动延迟一段时间再去做发送操作。

Q:为什么要分包发送?

BLE4.0蓝牙发送数据,单次最大传输20个byte,如果是一般的协议命令,如:开关灯、前进左右等等,是不需要
分包的,如果是需要发送如:图片、BIN文档、音乐等大数据量的文件,则必须进行分包发送,BLE库中已经提
供了发送大数据包的接口,需要的小伙伴可以去下载DEMO查看用法。

Service和Characteristic

Service是服务,Characteristic是特征值。蓝牙里面有多个Service,一个Service里面又包括多个Characteristic,具体的关系可以看图
service和characteristic的关系
图中画的比较少,实际上一个蓝牙协议里面包含的Service和Characteristic是比较多的 ,这时候你可能会问,这么多的同名属性用什么来区分呢?答案就是UUID,每个Service或者Characteristic都有一个 128 bit 的UUID来标识。Service可以理解为一个功能集合,而Characteristic比较重要,蓝牙设备正是通过Characteristic来进行设备间的交互的(如读、写、订阅等操作)。

开发

声明蓝牙BLE权限

<!--声明蓝牙权限-->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Android6.0系统以上开启蓝牙还需要定位权限,定位权限属于危险权限,需要动态申请,申请定位的代码省略。

android.Manifest.permission.ACCESS_FINE_LOCATION

/**
 * BLE 蓝牙连接
 */
public class BLEBlueToothUtil {
   

    private static final String TAG ="BLEBlueToothUtil" ;
    private FragmentActivity mActivity;
    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothGatt mBluetoothGatt; //非常重要,读、写、订阅等操作都需要用到这个对象

    private List<BluetoothDevice> mDeviceList = new ArrayList<>();
    private List<Integer> mRssiList = new ArrayList<>();

    //服务和特征值
    private UUID write_UUID_service; //写入bluetoothGattService的UUID
    private UUID write_UUID_chara;  //写入characteristic 特征值的UUID
    private UUID read_UUID_service; //读取bluetoothGattService的UUID
    private UUID read_UUID_chara; //读取characteristic 特征值的UUID
    private UUID notify_UUID_service; //订阅bluetoothGattService的UUID
    private UUID notify_UUID_chara;  //订阅characteristic 特征值的UUID
    private UUID indicate_UUID_service;
    private UUID indicate_UUID_chara;
    private String hex="7B46363941373237323532443741397D";

    private boolean mIsScanning = false;
    private boolean mIsConnecting = false;

    private BLEBlueToothUtil(){
    }

    public static BLEBlueToothUtil getInstance(){
   
        return BLEBlueToothUtilHolder.mInstance;
    }

    private static class BLEBlueToothUtilHolder{
   
        private static final BLEBlueToothUtil mInstance = new BLEBlueToothUtil();
    }

    BluetoothAdapter.LeScanCallback mScanCallback = new BluetoothAdapter.LeScanCallback() {
   
        @Override
        //device是设备对象,rssi扫描到的设备强度,scanRecord是扫面记录
        // 扫描过的设备仍然会被再次扫描到,因此要加入设备列表之前可以判断一下,如果已经加入过了就不必再次添加了。
        public void onLeScan(BluetoothDevice device, 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值