QsBle Android蓝牙框架之用链式操作让你的蓝牙开发更优雅

QsBle蓝牙操作框架,用kotlin协程带给你不一样的使用体验,Java也能食用

github链接

QsBle的链式使用

QsBle框架的的链式操作都必须先调用QsBle.getInstance().chain(mac)之后再开始进行操作,包括后面的协程功能也是对链式操作进行的封装,它的实现方式也是用的链式操作来实现的,所有链式的操作都是可以随意组合的,这种随意组合能给你在Java这种不支持协程的语言中体会到一种用同步的方式执行异步的操作的感觉,链式操作设置对应的参数之后调用start()方法,该链式操作就会开始执行了

下面的代码中将QsBle全局的对象简称为ble

关于协程和链式操作几个公共操作符的说明

这几个操作符的特点是所有链式操作都支持的,将各个操作符一起使用,能解决一些很复杂的场景

操作符1:dump(boolean dump)

含义:当前的链式段在执行失败时是否结束整条执行链,默认是true,结束整条链

链式操作:

//直接通过链式连接5个设备
QsBle.getInstance().chain()
    .connect(mac1)...
    .connect(mac2).dump(false)...
    .connect(mac3).dump(true)...//默认就是true,可以不设置
    .connect(mac4)
    .connect(mac5)
    .start()

说明:mac1连接成功后会去连接mac2,如果mac2连接失败,比如连接超时或者系统返回错误,那么mac3是否是否会继续连接,答案是会继续连接,因为mac2这条链的dump=false,即使连接失败也不会中断整条链,mac3会在mac2连接失败后继续执行连接操作,如果mac3也连接失败了呢?因为dump=true,直接中断整条链的执行,后面mac4和mac5就不会执行连接操作了

协程:

bleLifeScope.launch ({
    //直接通过链式连接5个设备
    QsBle.getInstance().chain().apply{
        connect(mac1).await()
        connect(mac2).dump(false).await()
        connect(mac3).dump(true).await()//默认就是true,可以不设置
        connect(mac4).await()
        connect(mac5).await()
    }
},onError = {
    //协程执行错误会调用,回调在主线程中
},onStart = {
   //协程执行开始之前会调用,回调在主线程中
},onFinally = {
    //不管协程执行成功还是失败,这个方法最终都会调用,回调在主线程中
})

说明:和上面链式执行流程是一样的,在协程中唯一不一样的就是,当这条链的dump=true时,发生连接失败,协程中会直接抛出异常,所以在执行到mac3连接失败时,会回调onErro函数,想要执行失败不抛出异常,那执行设备dump=false即可

操作符2:async())

含义:用过kotlin协程的应该都清楚这个操作符的作用,这个操作符的作用和协程的很相似,它的作用是将当前链异步执行,执行这条链的时候立刻返回成功执行下一条链,而不等待这条链的返回结果

链式操作:

//通过链式同时连接5个设备
QsBle.getInstance().chain()
    .connect(mac1).async()...
    .connect(mac2).async().dump(false)...
    //由于async是直接返回成功的,不存在失败,故dump的值多少对整条链的执行没有任何影响
    .connect(mac3).async().dump(true)...//默认就是true,可以不设置
    .connect(mac4).async()
    .connect(mac5).async()
    .start()

协程:

bleLifeScope.launch ({
    //协程中使用需要注意,即使你调用了async()操作符
    //但是后面接着调用了await()
    //那async()操作符将会失效
    /**
     * 所以执行流程是先等mac1连接成功
     * 当然mac1连接失败会直接抛出异常,因为dump默认为true
     * 接着同时连接后面的四个设备
     * */
    QsBle.getInstance().chain().apply{
        connect(mac1).async().await()
        connect(mac2).async().start()
        connect(mac3).async().start()
        connect(mac4).async().start()
        connect(mac5).async().start()
    }
},onError = {
    //协程执行错误会调用,回调在主线程中
},onStart = {
   //协程执行开始之前会调用,回调在主线程中
},onFinally = {
    //不管协程执行成功还是失败,这个方法最终都会调用,回调在主线程中
})

操作符3:delay(long delay)

含义:当前的链式段会延迟delay ms执行,注意指是当前链

链式操作:

QsBle.getInstance().chain()
    .connect(mac1)...
    .connect(mac2).delay(1000).dump(false)...
    .connect(mac3).delay(2000).dump(true)...//默认就是true,可以不设置
    .connect(mac4).delay(3000)
    .connect(mac5).delay(4000)
    .start()

协程:

bleLifeScope.launch ({
    QsBle.getInstance().chain().apply{
        connect(mac1).await()
        connect(mac2).await()
        //这个用的是kotlin协程提供的阻塞函数
        delay(1000)
        //这个使用的是框架自己实现的delay()操作符
        //虽然它们执行的结果和时间都是一样的,但是要区分
        connect(mac3).delay(2000).await()
        connect(mac4).await()
        delay(3000)
        connect(mac5).delay(4000).await()
    }
},onError = {
    //协程执行错误会调用,回调在主线程中
},onStart = {
   //协程执行开始之前会调用,回调在主线程中
},onFinally = {
    //不管协程执行成功还是失败,这个方法最终都会调用,回调在主线程中
})

操作符4:retry(int retry)

含义:当前的链式段执行失败重写执行次数

链式操作:

QsBle.getInstance().chain()
    .connect(mac1)...
    //retry操作符作用的是当前的链
    //如果mac2连接全部失败,会尝试重写连接3次
    .connect(mac2).retry(3)...
    //这里是对mac3重连3*3=9次,retry操作符是对当前链式段的重试
    .connect(mac3).reConnectCount(3).retry(3)...//默认就是true,可以不设置
    .start()

协程:

bleLifeScope.launch ({
    QsBle.getInstance().chain().apply{
        connect(mac1).await()
        connect(mac2).retry(3).await()
        connect(mac3).reConnectCount(3).retry(3).await()
    }
},onError = {
    //协程执行错误会调用,回调在主线程中
},onStart = {
   //协程执行开始之前会调用,回调在主线程中
},onFinally = {
    //不管协程执行成功还是失败,这个方法最终都会调用,回调在主线程中
})

操作符5:timeout(long timeout)

含义:当前的链式段在最大执行的时间,也就是超时时间

链式操作:

QsBle.getInstance().chain()
    /**
     *假设连接设备花费了5000ms,timeout=4000ms,那么这条链就会被判断执行失败,但是设备的状态是连接的
     *只是这条链是执行失败的
    **/
    .connect(mac1).connectTimeout(7000).timeout(4000)...
    .start()

协程:

bleLifeScope.launch ({
    QsBle.getInstance().chain().apply{
        //该协程最长会等待多久?
        //最长的情况下等待4000ms,只要超过了timeout的时间,还没有结果,就判断执行失败
        connect(mac1).timeout(4000).retry(3).await()
    }
},onError = {
    //协程执行错误会调用,回调在主线程中
},onStart = {
   //协程执行开始之前会调用,回调在主线程中
},onFinally = {
    //不管协程执行成功还是失败,这个方法最终都会调用,回调在主线程中
})

操作符6:withMac(String mac)

含义:当前的链式段及其后面执行的链对应的设备mac地址,这个参数除了扫描的链不是必须,其它的链都是必须的,只不过有的是隐式有的是显式

链式操作:

//以下面一个不恰当的例子为例
QsBle.getInstance().chain()
            //假设将下面的注释去掉,这条链执行到这会直接报错,因为没有默认的mac传入
            //.connect()
            .connect(mac1)
            .connect(mac2)
            //value0是发给mac2的,因为上游传入的是mac2
            .writeByLock(serviceUuid,chacUuid,value0).
            //value1是发给mac1的
            .writeByLock(serviceUuid,chacUuid,value1).withMac(mac1)
            //value2是发给mac1的
            .writeByLock(serviceUuid,chacUuid,value2)
            //value3是发给mac1的
            .writeByLock(serviceUuid,chacUuid,value3)
            //value4是发给mac2的,因为切换了mac的值
            .writeByLock(serviceUuid,chacUuid,value4).withMac(mac2)
            //value5是发给mac2的
            .writeByLock(serviceUuid,chacUuid,value5)
            //连接mac3
            .connect().withMac(mac3)
            //value6是发给mac3的
            .writeByLock(serviceUuid,chacUuid,value6)
            //value7是发给mac3的
            .writeByLock(serviceUuid,chacUuid,value7)
            //断开mac3,因为上游传入的是mac3
            .disconnect()
            //断开mac2
            .disconnect(mac2)
            //断开mac3
            .disconnect(mac3)
            .start()

协程:

参考上面提供的样例

蓝牙扫描

Android BLE的功能是从Android4.4开始支持的,在Android5.0后开始使用了新的扫描api,并且支持了BLE5.0,但是由于BLE的功能需要硬件的支持,Android不光在软件上是碎片化的,在硬件上的碎片化其实比软件更碎片,BLE的功能在不同的手机上使用的是不同的厂商芯片,这就导致了很多在应用层开发的代码在不同的手机上会出现不同的表现,这也是Android BLE开发的一个痛点,而且由于需要对一些老旧安卓手机的支持(即使是一些新的Android手机对BLE的一些功能支持的也不全),导致很多BLE的功能都是向最低兼容,QsBle扫描功能也是采取的此策略,最低兼容Android4.4,力求在不同软件和硬件的Android系统上保持扫描功能一致

全局扫描

//推荐方式
val scanCallback:IScanCallback= IScanCallback { device, rssi, scanRecord ->
    //扫描到设备回调
}
ble.addScanCallback(scanCallback)
ble.startScan(20000)
//在特定的时机移除回调
ble.rmScanCallback(scanCallback)

//不推荐方式
//这种方式其实也没什么问题,但是如果这个扫描回调不是全局,很有可能导致内存泄漏
//比如你在一个activity中扫描20000ms,但是10000ms后这个activity销毁了,这个回调却还被框架持有,会导致程序不可控
//这也是作者看了几个蓝牙框架的源码发现出现的共同问题,就是回调没有释放好导致内存泄漏问题
ble.startScan(20000, { device, rssi, scanRecord -> 
    
})
//但是如果你的扫描回调是和Lifecycle绑定的,那么该方法将在传入的lifecycle对象销毁时自动将回调移除
//这个也是推荐的
ble.startScan(20000,lifecycle, { device, rssi, scanRecord -> 
    
})

扫描指定mac地址的设备

ble.chain(mac)
     //扫描到了指定的mac设置会停止扫描
    .startScan()
    //扫描时间
    .scanTime(20000)
    //过滤的设备名称
    .filterName("deviceName")
    //过滤的serviceUuid
    .filterServiceUuids(serviceUuid)
    //是否重复回调
    .noRepeatCallback()
    .startScan()
    .before { 
        //这条链执行开始之前会回调
    }
    .after { 
        //这条链执行结束或者执行下一条链时会回调
    }
    .data { data:Entry<Integer, byte[]> ->
        //扫描到的设备信息
        //Integer:rssi
        //byte[]:广播字节数组
    }
    .error { 
        //执行错误会回调
    }
    //这条链在lifecycle destroy时自动释放资源
    //建议所有的链都和一个lifecycle对象绑定
    //可以防止操作不当引起的内存泄露问题
    .start(lifecycle)

连接设备

方式一:调用ble.connect()系列方法

/**
 * 连接设备
 * @param mac
 * @param timeout 连接超时
 * @param reconnectCount 连接失败重连次数
 * @param connectFailCallback 连接失败回调
 */
public IMessageOption connect(@NonNull String mac, long timeout, int reconnectCount, Function3<Boolean /*isTimeout*/,Integer /*status*/,Integer/*profileState*/> connectFailCallback)

方式二:链式调用

ble.chain(mac)
    //连接
    .connect()
    //连接超时
    .connectTimeout(7000)
    //重连次数
    .reConnectCount(3)
    //必须发现指定的service才绑定连接成功
    //防止某些时候连接建立,但是服务无法被发现
    .mustDiscoverService(serviceUuid)
    //连接失败回调
    .setConnectFailCallback { p1, p2, p3 ->

    }
    //连接状态改变回调
    .setConnectStatusChangeCallback { device, isConnect, status, profileState -> 
        
    }
    //发现服务回调
    .setServicesDiscoveredCallback { device, services, status -> 
        
    }
    .before { 
        //这条链执行开始之前会回调
    }
    .after { 
        //这条链执行结束或者执行下一条链时会回调
    }
    .data {Boolean->
        //连接状态
    }
    .error { 
        //执行错误会回调
    }
    //以上所有的回调都会在这条链执行完之后一并移除
    .start(lifecycle)

断开后的重连操作:
这个断开后的重连操作是指非程序员代码断开设备连接时会对设备进行重新连接
调用ble.setAutoReconnectCount()方法

/**
 * 设置非代码断开设备自动重连次数
 * 如果不是app端主动断开,会按照设置的值进行重连,默认是0,不进行重连
 * @param mac
 * @param autoReconnectCount 这个值会覆盖 BleGlobalConfig.autoReconnectCount 的默认设置
 */
public void setAutoReconnectCount(String mac,int autoReconnectCount)
//当取消自动重连时,只需要将autoReconnectCount传入0即可

写特征值(建议全都使用带NoRsp和带Lock的)

方式一:调用ble.writeNoRsp()系列方法

/**
     * 向一个特征值写数据
     * 有norsp和没有norsp的区别就像是Udp和Tcp协议的区别,udp效率肯定比tcp高,但是速度肯定不上udp
     * 实际测试中发现有norsp的比没有norsp的快3-30倍,和连接参数有关
     * norsp-->udp
     * rsp-->tcp
     * @param mac
     * @param serviceUuid
     * @param chacUuid
     * @param value 一个mtu大小的值,可以小于,但是不能大于,小于的话框架会自动补0凑成一个mtu大小的字节数组
     * @param retryWriteCount 一个mtu包的失败重写次数
     */
IMessageOption write(String mac,UUID serviceUuid, UUID chacUuid, byte[] value, int retryWriteCount);

IMessageOption writeNoRsp(String mac,UUID serviceUuid, UUID chacUuid, byte[] value,int retryWriteCount);

IMessageOption writeByLock(String mac,UUID serviceUuid, UUID chacUuid, byte[] value, int retryWriteCount, Function2<Boolean,Integer> writeCallbac);
/**
     * 向一个特征值写数据
     * 有norsp和没有norsp的区别就像是Udp和Tcp协议的区别,udp效率肯定比tcp高,但是速度肯定不上udp
     * norsp-->udp
     * rsp-->tcp
     * @param mac
     * @param value
     * @param serviceUuid
     * @param chacUuid
     * @param value 这个value的大小不限制,内部所有发送的带lock的消息都是进入一个写队列中,一个一个排队发送,收到操作系统发送成功的回调
                    后会继续发送下一个数据包,我建议只要你向该特征值发送的包有大于一个mtu的,都使用带lock的方法,能够避免很多问题,比如撞包,除非你的
                    数据都是不超过一个mtu的,带lock的方法我建议使用带norsp的,发送速度会较快
     * @param retryWriteCount 一个mtu包失败重写次数
     * @param writeCallback 所有数据发送完成或者没有发送完成,都会回调该方法,这个方法主要是发送该数据包的状态回调,该方法能确保被回调
     */
IMessageOption writeByLockNoRsp(String mac,UUID serviceUuid, UUID chacUuid, byte[] value,int retryWriteCount, Function2<Boolean,Integer> writeCallback);

方式二:链式调用

ble.chain(mac)
    //保证设备连接的情况下
    //在设备连接时,会跳过连接直接执行下面的操作
    .connect()
    //保证通知是打开的情况下
    .openNotify(serviceUuid,notifyUuid)
    //写value1
    .writeByLock(serviceUuid,chacUuid,value1)
    //value1写成功后继续写value2
    .writeByLockNoRsp(serviceUuid,chacUuid,value2)
    ....
    .writeByLockNoRsp(serviceUuid,chacUuid,valueN)
    .start(lifecycle)

读特征值,读描述,写描述,打开通知,取消通知,写文件等等

这些操作都是和以上是同理的,只是对于常用的功能的使用,我单独列出来,这些功能我直接放在一起讲解

/**
 * 连接设备
 * @param mac
 * @param timeout 连接超时
 * @param reconnectCount 连接失败重连次数
 * @param connectFailCallback 连接失败回调
 */
public IMessageOption connect( String mac, long timeout, int reconnectCount, Function3<Boolean /*isTimeout*/,Integer /*status*/,Integer/*profileState*/> connectFailCallback) 

/**
 * 断开连接
 * @param mac
 */
public IMessageOption disconnect( String mac) 
    return ble.disconnect(mac);
}

public IMessageOption writeFile( String mac,  UUID serviceUuid,  UUID chacUuid,  byte[] fileBytes,  IOtaUpdateCallback otaUpdateCallback)

/**
 * 写一个文件,传入一个File对象
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 * @param file 注意AndroidQ的存储沙箱机制
 * @param otaUpdateCallback
 */
@Nullable
public IMessageOption writeFile( String mac,  UUID serviceUuid,  UUID chacUuid,  File file,  IOtaUpdateCallback otaUpdateCallback) catch (FileNotFoundException e) 

/**
 * 传入一个io流,会将io流中的所有字节按顺序依次发送给设备
 * 相较于writeFileNoRsp方式写,这种方式写一个mtu的速度比writeFileNoRsp的速度慢大概3-30倍
 * 我建议所有的写操作都使用NoRsp类型,除非是特别指定必须保证到达的数据
 * 有norsp和没有norsp的区别就像是Udp和Tcp协议的区别,udp速度肯定比tcp高,但是不能保证数据在传输过程的丢失重发
 * norsp-->udp
 * rsp-->tcp
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 * @param fileByteCount
 * @param segmentSize
 * @param datasource
 * @param otaUpdateCallback
 */
public IMessageOption writeFile( String mac,  UUID serviceUuid,  UUID chacUuid, int fileByteCount, int segmentSize,  InputStream datasource,  IOtaUpdateCallback otaUpdateCallback)

public IMessageOption writeFileNoRsp( String mac,  UUID serviceUuid,  UUID chacUuid,  byte[] fileBytes,  IOtaUpdateCallback otaUpdateCallback)

@Nullable
public IMessageOption writeFileNoRsp( String mac,  UUID serviceUuid,  UUID chacUuid,  File file,  IOtaUpdateCallback otaUpdateCallback) catch (FileNotFoundException e) 

public IMessageOption writeFileNoRsp( String mac,  UUID serviceUuid,  UUID chacUuid, int fileByteCount, int segmentSize,  InputStream datasource,  IOtaUpdateCallback otaUpdateCallback)

public IMessageOption write( String mac,  UUID serviceUuid,  UUID chacUuid,  byte[] value, int retryWriteCount) 
/**
 * 向一个特征值写数据
 * 有norsp和没有norsp的区别就像是Udp和Tcp协议的区别,udp效率肯定比tcp高,但是速度肯定不上udp
 * norsp-->udp
 * rsp-->tcp
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 * @param value 一个mtu大小的值,可以小于,但是不能大于,小于的话框架会自动补0凑成一个mtu大小的字节数组
 * @param retryWriteCount 一个mtu包的失败重写次数
 */
public IMessageOption writeNoRsp( String mac, UUID serviceUuid,  UUID chacUuid,  byte[] value, int retryWriteCount) 

public IMessageOption writeByLock( String mac,  UUID serviceUuid,  UUID chacUuid,  byte[] value, int retryWriteCount, Function2<Boolean, Integer> writeCallback) 

/**
 * 向一个特征值写数据
 * 有norsp和没有norsp的区别就像是Udp和Tcp协议的区别,udp效率肯定比tcp高,但是速度肯定不上udp
 * norsp-->udp
 * rsp-->tcp
 * @param mac
 * @param value
 * @param serviceUuid
 * @param chacUuid
 * @param value 这个value的大小不限制,内部所有发送的带lock的消息都是进入一个写队列中,一个一个排队发送,收到操作系统发送成功的回调
                后会继续发送下一个数据包,我建议只要你向该特征值发送的包有大于一个mtu的,都使用带lock的方法,能够避免很多问题,比如撞包,除非你的
                数据都是不超过一个mtu的,带lock的方法我建议使用带norsp的,发送速度会较快
 * @param retryWriteCount 一个mtu包失败重写次数
 * @param writeCallback 所有数据发送完成或者没有发送完成,都会回调该方法,这个方法主要是发送该数据包的状态回调,该方法能确保被回调
 */
public IMessageOption writeByLockNoRsp( String mac,  UUID serviceUuid,  UUID chacUuid,  byte[] value, int retryWriteCount, Function2<Boolean, Integer> writeCallback) 

/**
 * 写描述,你得确定该描述是否属性可写
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 * @param descUuid
 * @param value
 */
public IMessageOption writeDesc( String mac,  UUID serviceUuid,  UUID chacUuid,  UUID descUuid,  byte[] value) 

/**
 * 读特征值,你得确定该特征是否可读
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 */
public IMessageOption read( String mac,  UUID serviceUuid,  UUID chacUuid) 

/**
 * 读描述,你得确定该描述是否可读
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 * @param descUuid
 */
public IMessageOption readDesc( String mac,  UUID serviceUuid,  UUID chacUuid,  UUID descUuid) 

/**
 * 打开通知
 * 内部会自动判断是打开ENABLE_NOTIFICATION_VALUE还是ENABLE_INDICATION_VALUE类似
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 */
public IMessageOption openNotify( String mac,  UUID serviceUuid,  UUID chacUuid) 

/**
 * 关闭通知
 * @param mac
 * @param serviceUuid
 * @param chacUuid
 */
public IMessageOption cancelNotify( String mac,  UUID serviceUuid,  UUID chacUuid) 

/**
 * 读rssi
 * @param mac
 */
public IMessageOption readRssi( String mac) 

/**
 * 设置mtu,设置时需要注意,有点设置返回的虽然时成功,但是也有可能用这个mtu时无法通信的,这个不光需要
 * 手机软件和硬件的支持还需要设备端软件和硬件的支持
 * 我建议最好统一设置20byte长度,这也是系统默认的
 * @param mac
 * @param mtu
 */
public IMessageOption setMtu( String mac, int mtu) 

/**
 * Android5.0开始从软件上支持Ble5.0,但是手机的硬件是否支持Ble5.0要看各个手机的配置,故对Ble5.0的很多操作在很多的手机上是无效的
 * 包括设置物理信道的方式
 * @param mac
 */
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public IMessageOption readPhy( String mac) 

/**
 *
 * @param mac
 * @param txPhy 发送的物理信道 {@link android.bluetooth.BluetoothDevice#PHY_LE_1M_MASK} 这条信道相对发的慢一点
 *             or {@link android.bluetooth.BluetoothDevice#PHY_LE_2M_MASK} 这条信道相对发的快一点
 *             or {@link android.bluetooth.BluetoothDevice#PHY_LE_CODED_MASK} 使用ble5.0信道编码的方式,这种要配合设置phyOptions参数,特点是发的远,收的远,Android5.0以下或者不支持ble5.0的硬件不支持设置
 * @param rxPhy 接收的物理信道 {@link android.bluetooth.BluetoothDevice#PHY_LE_1M_MASK} 这条信道相对发的慢一点
 *      *             or {@link android.bluetooth.BluetoothDevice#PHY_LE_2M_MASK} 这条信道相对发的快一点
 *      *             or {@link android.bluetooth.BluetoothDevice#PHY_LE_CODED_MASK} 信道编码方式,Android5.0以下或者不支持ble5.0的硬件不支持设置
 * @param phyOptions {@link android.bluetooth.BluetoothDevice#PHY_OPTION_NO_PREFERRED}  默认的编码方式,android5.0以下和不支持ble5.0的设备
 *                  or {@link android.bluetooth.BluetoothDevice#PHY_OPTION_S2} ble5.0的,收发距离相较于ble4.x,远2倍 功耗较高
                    or {@link android.bluetooth.BluetoothDevice#PHY_OPTION_S8} ble5.0的,收发距离相较于ble4.x,远4倍 功耗特别高
                注:除了你手机支持ble5.0的,你连接的设备也需要支持ble5.0才能设置成功,所有这条设置对于不是特别了解ble的不需要关注,全部默认就行,因为各个手机各个设备
                    所支持的硬件也是不同的
 */
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public IMessageOption setPreferredPhy( String mac, int txPhy, int rxPhy, int phyOptions) 

//参考值:interval=12 latency=0
//发送和接受数据速度很快,耗电量高
//{@link BluetoothGatt.CONNECTION_PRIORITY_HIGH}

/**
 * 连接间隔和设备时延已经超时时间影响着数据的收发速度和手机的功耗
 * @param mac
 */
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public IMessageOption requestConnectionToHigh( String mac) 

//参考值:interval=40 latency=0
//发送和接受数据度一般,耗电量一般
//{@link BluetoothGatt.CONNECTION_PRIORITY_BALANCED}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public IMessageOption requestConnectionToBalanced( String mac) 

//参考值:interval=100 latency=2
//发送和接受数据度较慢,耗电量低
//{@link BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public IMessageOption requestConnectionToLowPower( String mac) 

public IMessageOption startScan(long time,Lifecycle lifecycle, IScanCallback callback) );
    return ble.startScan(time, callback,null);
}
/**
 * 开始扫描
 * @param time 扫描时间
 * @param callback 扫描回调
 * @param config 扫描过滤配置
 */
public IMessageOption startScan(long time, IScanCallback callback, SimpleScanConfig config) 

//android5.0以下版本无法生效
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public IMessageOption startScanOnlyLollipop(long time, List<ScanFilter> filters, ScanSettings settings, IScanCallback callback) 

链式操作

这些链式随意组合加上提供的操作符,你能将很多以前实现很复杂的功能变得简单甚至只需要一行代码就能实现

ble.chain(mac)
    .startScan()
    .data { 
        //扫描的数据
    }
    .connect()
    .openNotify()
    .read(serviceUuid,readUuid)
    .data { ByteArray->
        //读的特征值
    }
    .readRssi()
    .data { Int->
        //rssi
    }
    .readDesc(serviceUuid,chacUuid,descUuid)
    .data { ByteArray->
        //读的描述
    }
    .readPhy()
    .data { IntArray->
        //读的信道
    }
    //设置BLE5.0的信道
    .setPreferredPhy()
    //设置连接参数
    .requestConnectionToLowPower()
    .requestConnectionToBalanced()
    .requestConnectionToHigh()
    .writeByLock()
    .writeByLockNoRsp()
    ...等
    .start(lifecycle)

还有更多的解放你双手的功能还没有介绍完全,作者从事物联网行业多年,作为开发了Android蓝牙多年的程序员,清楚的知道很多蓝牙开发的痛点和坑点,也用过目前较火的几个框架,这些框架大多都是Android旧时代的产物加上很多都没有继续维护,导致很多功能实现起来非常的麻烦,作者写的QsBle框架就是为了解决这些痛点的,希望大家多多支持,如果觉得可以,github上点个star也是对作者的支持和鼓励

github链接

关于QsBle对Android BLE的协程以及Ota分包组包的等操作,我后面会单独写几篇篇文章介绍,敬请期待

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值