美文网首页
Android低功耗蓝牙踩坑填坑记录

Android低功耗蓝牙踩坑填坑记录

作者: Winter_F | 来源:发表于2018-12-26 17:11 被阅读62次

Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的 API, 应用程序可以通过这些 API 执行扫描蓝牙设备、查询 services、读写设备的 characteristics(属性特征)等操作。

关于BLE的基础介绍和API使用说明,可以参照其他文章(比如这篇《Android BLE 蓝牙开发入门》就写的不错),本文主要关注和记录开发过程中遇到的坑和解决办法。

关于操作线程

扫描、连接、读写等全部蓝牙操作都应该在同一个线程中执行,否则在不同手机上可能会出现各种奇奇怪怪的错误。如果业务操作不频繁,没有要求同时操作多个蓝牙设备的话,可以简单一点处理,把操作都放在UI线程中执行,但更优的方法是创建一个新线程,所有操作都在这个线程中执行。

关于蓝牙状态

扫描蓝牙设备前需要做几步常规操作:

  1. 检查设备是否支持蓝牙
val manager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val adapter = manager.adapter
if(adapter == null){
    toast("设备不支持蓝牙")
}
  1. 6.0以上系统,需要请求 ACCESS_COARSE_LOCATION 权限才能扫描附近的蓝牙设备
ActivityCompat.requestPermissions(
    this,
    arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
    REQUEST_PERMISSION_LOCATION
)
  1. 6.0以上系统,需要打开定位服务才能扫描到蓝牙设备,部分手机还只有开启高精度定位才能扫描,可以用文字提醒用户去开启
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivityForResult(intent, REQUEST_LOCATION)
  1. 打开蓝牙开关
val manager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val adapter = manager.adapter
if(!adapter.enable()){
    val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
    startActivityForResult(intent, REQUEST_BLUETOOTH)
}

关于扫描方法

系统提供了两个扫描蓝牙设备的,分别是BluetoothAdapter和BluetoothLeScanner,其中BluetoothAdapter又提供了两个搜索蓝牙设备的方法startDiscovery、startLeScan,它们的区别如下:

方法 API 搜索范围 效率 其他特点
BluetoothAdapter.startDiscovery 18 经典蓝牙和低功耗蓝牙 搜索低功耗蓝牙的效率很低
BluetoothAdapter.startLeScan 18 低功耗蓝牙
BluetoothLeScanner 21 低功耗蓝牙 接口更灵活

对于BLE设备,如果API支持,最好使用BluetoothLeScanner,否则,应该使用效率更高的startLeScan方法。

val bluetoothManager = mContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val adapter = bluetoothManager.adapter
// 方法一
adapter.startDiscovery()
// 方法二
adapter.startLeScan(mLeScanCallback)
// 方法三
adapter.bluetoothLeScanner.startScan(scanCallback);

关于资源消耗

蓝牙搜索非常消耗电量,在搜索到目标设备时,应该马上停止搜索操作,可以通过设置一个定时器来定时停止扫描操作。

val overtime = 10000; //10秒超时
Flowable.timer(overtime, TimeUnit.MILLISECONDS, Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
        stopScan()
    }, Throwable::printStackTrace)

关于连接设备

连接设备时,系统会返回一个操作对象Gatt,这个Gatt有几个比较坑的点:

  1. 连接设备时要设置一个BluetoothGattCallback的回调,只有关闭Gatt才能同时关闭回调,如果只是断开了Gatt连接而没有关闭,后续连接上同一个设备时会把全部Callback都回调一次
  2. Gatt提供了重连功能,但部分手机上重连后会发现不到service
  3. 系统支持的连接句柄数是有限的,当达到上限后无法再建立新的连接,所以Gatt在不用时要及时关闭

结合上面三点,最好的使用方式是断开连接后马上关闭Gatt,重新连接时不要使用重连功能,而是创建一个新的Gatt对象。

// 连接设备
mGatt = device.connectGatt(mContext, false, mConnectGattCallbackImpl)

......

// 断开连接
mGatt.disconnect()
mGatt.close()

关于发现服务

部分手机上 discoverServices 的操作很慢,甚至一直不回调 onServicesDiscovered 方法,原因可能是connect interval太大。根据从网上找到的资料,有的手机会主动向设备发起更改connect interval,而有的手机却不会,这样的话connect interval相差就会很大,有的手机是7ms,有的手机是默认的50ms,所以发现service都要8s,甚至20s。
对此,我们有两个解决方法:

  1. 建立连接后,手动修改连接间隔
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    mGatt!!.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
}
  1. 对 discoverServices 方法进行超时检查,一段时间内没有回调 onServicesDiscovered 的话,就重新执行发现服务的操作

实际上,以上两个解决方法是不冲突的,可以同时使用。

关于Notify开关

Gatt提供了开关 Notify 的方法,但对于部分蓝牙设备来说并不足够,我们还必须对Descriptor写入一个数值才能真正完成开关 Notify 的操作,同时要注意,等到onDescriptorWrite被回调才能执行下一个操作。

    private val CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID =
        UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
//设置Char的notify开关
var result = mGatt?.setCharacteristicNotification(char, enable)!!
if (result) {
    val descriptor = char.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID)
    descriptor?.value = if (enable)
        BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
    else
        BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
    result = result && mGatt?.writeDescriptor(descriptor)!!
}

相关文章

  • Android低功耗蓝牙踩坑填坑记录

    Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗...

  • 微信小程序之蓝牙 BLE 踩坑记录

    调用蓝牙BLE步骤初始化蓝牙---搜寻附近的蓝牙外围设备---监听寻找到新设备的事件---连接低功耗蓝牙设备 踩坑...

  • Android零散技术点

    Android BLE 蓝牙开发入门 逐步指导新手进行 Android ble 蓝牙的开发,避免踩坑。Androi...

  • Android BLE(低功耗蓝牙)踩坑学习

    从 github 下载 google 的蓝牙样例代码学习 ( https://github.com/android...

  • Android 日常踩坑

    1、Android NDK开发踩坑 踩坑环境 Android Studio 3.4.1,JDK 1.8 1、为什么...

  • Android之音频焦点

    Android之音频焦点 车机开发很多应用涉及到音频,音频焦点踩过很多坑,也填过很多坑,蓝牙,多媒体音视频,ipo...

  • Android填坑记录

    主要记录Android开发当中一些容易引起错误的,又不容易发现的问题。 Activity方面 1,onWindow...

  • Android 填坑记录

    1.使用Fresco时,根据图片url获取缓存文件 2.使用Fresco时,如何实现缩放效果(PhotoView)...

  • 你有填坑与补位的能力吗?

    踩坑,我们经常听说,而填坑听过吗?补位又是什么? 人生路上不免踩坑,踩了无数坑,走了无数弯路,什么时候才能到我们想...

  • iOS与蓝牙间的通讯格式问题

    这段时间在做iOS端关于蓝牙设备间的通讯功能,入了不少坑,在此把入坑填坑的过程讲一讲。 第一坑,蓝牙方面用的是晟睿...

网友评论

      本文标题:Android低功耗蓝牙踩坑填坑记录

      本文链接:https://www.haomeiwen.com/subject/sqaflqtx.html