18年最新汇总: Bluetooth-那些年我们一起踩过得坑

叁叁肆2018-10-25 12:29

此文已由作者王诚志授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


文章的编写源于本周的一次分享。因为业务架构的调整与需要。我们项目扩大了项目开发上的支持,加大了人员投入。把其他项目的童鞋一块来入蓝牙开发的刀山火海来。因此,作为蓝牙开发的经验人员。安卓和iOS一起准备了这次蓝牙开发的坑爹汇总分享。掉进坑了,不能一个人痛,得把隔壁也拉进来瞅瞅~


分享整理了蓝牙开发期间遇到的坑。其中, 安卓踩坑部分由 @文浩 整理提供,在文章中一块汇总记录了。



公共坑点


1.蓝牙需要在主线程中执行


蓝牙的数据通讯是串行异步回调的。所有操作必须在主线程执行,且一段时间内仅支持单个设备的数据发送。类似于系统的并行操作,而不是并发。不在主线程中执行会,蓝牙可能不操作或几十秒后才执行。


iOS 坑点


1. CBCentralManager 系统蓝牙管理类必须设计为单例!!!


(最新 iOS 12 系统升级日志标明修复了部分蓝牙bug,  不知道是否包含这个问题,待验证。)在系统Api的文档 和 说明上, 并没有具体说明要求蓝牙管理类要设为单例。那么为什么这里要这么强调呢。这就涉及到 iOS 蓝牙系统的坑了。


Apple 其实在蓝牙框架的设计上, 本来就没有要求他是单例。这是一个iOS系统的BUG。具体原因如下,在官方社区就能找到答案。


 Quesion: 
    Why Bluetooth sometimes reported as OFF when it's actually turned on?
    Only toggling Bluetooth resolved the issue in Control Center.

  Reply:
  When Bluetooth is toggled in Control Center it enables blacklistMode which can be seen in the provided log. This allows devices/apps with permission to remain connected (example: Apple Watch) while disconnecting others. When toggling it back on, their is a bug that does not fully disable blacklistMode. Apps will then be told Bluetooth is still off when it isn't.

  The creation of a new session while in blacklistMode seems to be part of what causes the issue so the best way we have found to mitigate the effect of this issue is to not create a new central session on Bluetooth state change. Only create a new session when your app is opened or if it is closed and re-opened. It's still possible to encounter this but it will happen far less frequently as it requires a few extra steps to manifest. Otherwise once experiencing  the issue, toggle Bluetooth in Control Center once or twice and you should recover the connection.

我知道大家的都比较宝贵时间(懒),不想看着大段的文字说明,就直接贴上Google翻译啦。



从上面可以看出,蓝牙的状态获取上有BUG。有时需要重新关闭并激活控制中心的蓝牙开关才能让机器识别出正确的状态。更坑的是,(划重点)使用单例仅仅是能减少发生这个bug的频率,而不能杜绝。所以,该有的坑,还是会有。说不定什么时候和H5联调Api调用就遇到了这个坑爹的问题。


产品: 你这的蓝牙连接有个BUG,有时状态不对。


程序员 (黑人问号脸???): 对方不想和你说话并掏出一篇原文地址: https://forums.developer.apple.com/thread/92997


2.蓝牙的系统的开关其实有两个


第一个是在设置中的蓝牙开关,第二个是下面允许新的连接。



允许新的连接和控制中心的开关相关联。他的状态与控制中心的快捷快关一致。



这是在iOS10 还是11(具体忘了)添加的快捷按钮。他的作用是,暂时的关闭蓝牙一天。但这在用户认知和 APP处理上就造成了一个大坑:


我们在蓝牙交互弹出时会让用户跳转至设置页。但是假如用户只是使用控制中心的关闭一天开关。在设置中蓝牙的总开关是绿色并打开的。用户很难还需要打开允许新的连接开关才能使用。这就需要 APP 做额外的图示提醒操作了。


3.获取mac地址的坎坷之路


在iOS蓝牙框架中,另一个大坑就是不能直接获取蓝牙的mac地址。系统以安全性的名义给你隐藏了。你只能通过两种方案解决。


(1)硬件蓝牙广播数据段中写入蓝牙地址


这种方案 体验好,连接快,,开发处理简单,但需要硬件工程师的支持,在蓝牙广播中把蓝牙地址给你暴露出来。但对于那些商品先行走量,先售卖后再加入APP的硬件产品就没办法了。为了统一性就只能走恶心的方案二了。


所以在蓝牙前,最好能知会项目中的商品人员,达成这方面的共识。在商品验货时,要求硬件商在蓝牙广播加入蓝牙地址。


(2)180A 服务的 2A23 特征值


至于第二种方案,是没有办法中的办法。连接慢,处理复杂,多设备情况还容易强占连接。


在蓝牙的业界标准中,其实设备中是有记录硬件信息的服务的。我们可以从那里获取蓝牙地址。具体位置在service 180A (Device Information),characteristic 2A23。


但是这类方案是有后遗症的,很容易触发连接抢占的行为。要获取特征值就必须连接蓝牙。因此这种方案需要把周围的蓝牙,全部都连接一遍。慢且不说,当有两台手机时,A手机连接上了设备会导致B手机连接不到 。而B手机也在搜索,又导致A手机连接不上。两个互相抢设备的连接。就会有一堆坑点情况出现。


那么,如果硬件上不但没发蓝牙,还还没有遵守业界的蓝牙标准怎么办呢?


那就只有。。。去问神奇海螺咯 :-)


4.蓝牙连接上限的限制


蓝牙是有连接上限的,这个仔细想象也知道,肯定构不成坑点。最坑的是手机达到连接上限时,并不会如你所愿,只是最新的蓝牙连接不上了。而是系统会报XPC错误。并且蓝牙管理类挂掉。。。之前正常的连接也用不了了。



现在我们产品的解决方案是, 在给蓝牙产品使用的 vc会持有一个记录列表, 记录在页面内连接的所有蓝牙设备。当他被系统回收时,断开在当前界面连上的所有蓝牙连接。


5.搜索service必须先连接设备


这个算是小坑了。主要是搜索设备需要先连接上蓝牙。不然不会进行搜索。但是你强行调用接口也不会报错。


6.搜索Services 第一次搜索可能会回调两次,第一次为空


这是在与H5童鞋联调的时候发现的坑。并不是比现的,取决于蓝牙连接和你Api调用的时间快慢。当你拦截上蓝牙后快速调用搜索服务Api,有概率第一次搜索出来的服务为空数组,然后过一小会再回调一个有服务的非空服务列表。只在蓝牙处理不及的时候发生。


7.多设备连接service会触发多次回调


这个也是搜索服务的一大坑点。当一个APP连接多个蓝牙时,搜索服务一个搜索信息会回调多次代理接口。


如, APP 连接 A, B, C 三台设备。A调用一次搜索接口,收到的可能有这些回调。发现A服务(空数组),发现A服务(有值),发现A服务(有值),发现A服务(有值)。直接回调了4次。。。空数组情况参靠第5个坑点。


8.APP 只能控制在 APP 中发现的蓝牙



这个不太能算坑。在iOS系统中,会有所属的概念。就是A app连接的设备, B App 不能处理。所以如果设备是通过系统的蓝牙设置连接上的。我们的app是不能处理的。


要注意的是: 即使是同一个APP,alpha版本连接上了。Beta 版本同一个APP也是控制不了的。之前就给 QA 报过BUG, 发现是使用alpha APP连接蓝牙,然后通过微信登录跳到了Beta APP,发现控制不了设备的问题。


9.外设改变名字后不能取 name,要抓广播包 CBAdvertisementDataLocalNameKey


蓝牙设备默认是可以修改名字的,但问题是修改名字后,设备下次连接的name属性不会变(可能系统有缓存)。建议是取广播数据段 CBAdvertisementDataLocalNameKey的值作为判断比较准。


10.小Tips:蓝牙搜索设备Api可否重复的开关


这点就不是坑了,只是蓝牙搜索时是有去不去重的开关, key 是 CBCentralManagerScanOptionAllowDuplicatesKey。


安卓坑点


1.指令过快会有蓝牙粘包问题


猜测是安卓系统在蓝牙接收数据的处理上有时间的设置。如果硬件发送给设备端的速度太快。前一个包可能会和后一个包粘连。


例如接收A,B,C。过快时会受到 A+B前半段, B后半段+C两个包。时序上没有错误。但包之间会粘连。包的数量也会变化。


业界上的处理方案有四种:


第一种则是要求硬件设置一个最低发送间隔。直接从源头处理。


第二种是要求厂商发送必须沾满20字节。这样包被沾满了,就不会粘连。


第三种是协议上商讨一个结束位。通过结束位判断。但是需要耗费一个字节作为结束标记。


第四种是APP进行流式处理。APP先接收数据,然后拼接进行一个一个指令的切分再处理。


2.使用蓝牙功能需要定位权限


蓝牙的使用是需要定位功能的。猜测是类似于WiFi的限制一样。你能从周围的WiFi设备信号,定位推断出当前的位置。一次类推到蓝牙。


3.部分手机需要在系统中开启高精度定位


部分手机使用蓝牙时需要打开设置的高精度定位设置才能使用。具体比例不高。


4.蓝牙数据长度限制会限制20字节


系统的蓝牙不会对数据做切分。需要保证每条发送数据在20字节以内。多了会被截断。


Api 平台化的异端问题


1.系统返回service的uudid不一致


这个是系统处理上的坑点。安卓会返回蓝牙服务的32位序列。但是在iOS中,如果是以蓝牙标准结尾的服务,只会返回前几位,自动帮你切除后面标准的uuid。


2.蓝牙权限申请表现不一致


在蓝牙权限上,iOS智能建议用户跳转设置修改。而安卓能直接请求权限去控制连接蓝牙。因此需要额外处理。


网易云免费体验馆,0成本体验20+款云产品! 


更多网易技术、产品、运营经验分享请点击


相关文章:
【推荐】 网易考拉海购:电商高并发架构设计的铁律