提示:
1,本篇文章是本人凭4年经验创作,如果有什么不正确的地方,请您提出,我改正。
2,文章中所涉及到的思想,在我的开源库中都有体现。喜欢的话给个start,如果您在使用过程中遇到任何问题,我们可以一对一进行沟通。
(1)基本介绍
iOS与硬件交互,最常用就是蓝牙和WiFi技术。WiFi的话这里不做介绍。目前最主流的还是蓝牙。用蓝牙传输数据,一般手机会主要主设备,外围设备会作为从设备。当然也有手机作为从设备的情况,由于我经验着这方面没有实战经验,这里也不介绍。
这张图片我想它非常生动地表达了主设备和从设备之间的关系。主设备(手机)想要得到数据,从设备(手表,心率带)作为数据的提供方。只要遵循一定的协议,他们两个就会有数据交互。
主设备从字面意思我们也可以知道,他是拥有主动权的。所以应该是它主动去寻找从设备,主动连接从设备,如果连接上了,那就好说了,就是他们两个之前的事情了。
从设备就什么都不用管了吗?这里我要说不是。因为如果什么都不用管的化,那么主设备寻找到一个桌子、杯子,和寻找到一个手环、心率带怎么区别呢。所以从设备作为被搜索的设备就需要一个东西来标识我是被搜索的设备。所以就有了广播数据。从设备会时时刻刻发出这个广播,只要主设备发出寻找的命令。马上就会找到这个设备。(就好比火车站等人,从设备相当于外面举牌子的人,一直举着牌子等待别人接。)
这张图形象的表示了从设备主动向主设备发送广播。所以我们要有一个类来作为左边的主设备,一个类来作为右边的从设备。有了这两个类,他们就可以实现通讯了吗? 是的。当你看到后面的时候,你就会觉得怎么还有这么多类呢? 这里就像我们刚刚接触面对对象编程的思想一样。一辆车是一个类,车里面有发动机,有4个轮子等,他们都是特例的类。其实放着这里也是一样。所以在往后看的时候抓住这一个原则,就是一个主设备与一个从设备进行数据交互。其他的类都是锦上添花。
(2)API使用总结
我们已经知道了,蓝牙开发就是主设备和从设备的两个类来进行数据交互。那么是怎样进行数据交互的呢?
1,由于蓝牙比较特殊,他需要使用系统的蓝牙。所以就有了权限请求,后台模式设备,系统蓝牙有没有打开的情况。
2,由于是通过无线方式,所以就会有失败的情况,断开连接的情况。
3,也会有推到后台,被系统杀死的情况。(这个情况比较难处理,我会在最后的一遍文章中讲这些)
这里有三个新的概念:服务,特征,描述 他们是被包含关系。一个从设备中有很多服务,服务中有很多特征,特征中有描述。其实这样也不难理解。因为你想想,比如一个手环,他有自己的硬件信息(制造厂商,设备标识等),有需要发送出去的数据(步数,卡路里等),还有显示的时间,屏幕亮度调节,个人信息,主设备会读取/设置信息。如果全部放到一个服务里面就是显得非常乱。所以硬件就这样设计了三样东西。
我们需要关注的最多的应该是特征。你就可以这样去理解:服务只是为了分类特征(可以比方成文件夹一个)。特征才是一个一个我们需要的文件,需要对他进行读写操作。描述只是为了对这个文件的补充信息,也是可以读写的。
排除上面的几种情况,一个正常的交互流程是怎样的呢。
主设备:
(1)系统蓝牙准备。如果没有这个权限,接下来的事情都没法做。
(2)主设备扫描。主设备会扫描身边所有有广播发出的从设备。
(3)连接设备。从设备发送出来的广播会带有名称、RSSI等很多信息,主设备会根据这些信息选择其中的一个(或多个)连接上。
(4)停止扫描。这个时候,如果是发现了想要连接的设备就可以停止扫描了,因为再扫描下去的话,没有意义了。
我认为这里应该可以画一条线。因为两个设备已经建立了连接。接下来的事情就是他俩的事情了,与外界没有任何关系(接下来就是专心做好数据的交互)。如果你操作数据的时候没有实时要求,这里你就可以停下来,等需要进行数据交互的时候才进行下面的操作。
连接上设备之后,你可以理解和主设备就没什么事了,接下来的操作都是从设备读写特征。但是主设备还不能销毁。因为他是管理者,要保持连接,让从设备自己去读写数据。
从设备
(5) 获取服务。设备调用扫描服务的方法,得到设备上所有的服务。一旦扫描完成,就是保存在services属性上。
(6)获取特征。扫描完服务,可以继续扫描服务上的特征,也可以等到需要操作这个特征的时候再去寻找。
(7)发现描述。这一步可要可不要。描述是对特征信息的进一步补充。
(8)打开通知端口。因为从设备发过来的数据,一般都是只有打开了通知端口才能从代理中接受数据。
(9)读、写服务数据。也就是发送一些命令给从设备,让从设备改变某些属性,或读取某些有用的值。这个应该是最常用的一个操作了。
(10)断开设备。如果所有的交互事情已经做完,或者退出界面。这是必须要做的一步操作。要不从设备就会一直被主设备连着,浪费资源。
一句话来讲就是:主设备发现并连接设备。从设备从特征中读写数据。(如果你能读懂这句话,那么基本上理解了蓝牙的工作原理。)
如上10个步骤所用到的方法和回调先写出来。让大家先有个基本的了解。
这里还要普及一个知识点我们都知道,蓝牙操作都是需要一定时间的,也就是都是异步操作(比如连接设备,不可能同步回到出来是否连接成功)。iOS处理这些操作一般都是block,delegate,kvo,通知。这里coreBluetooth采用了最常见的一种方式(delegate)。所以就有了主设备代理(CBCentralManagerDelegate),和从设备代理(CBPeripheralDelegate)。他们两所有的操作结果都是在这两个代理方法中返回。
(1)系统蓝牙准备。
- initWithDelegate: queue: options:
如果系统蓝牙发生改变。会触发如下代理方法。
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
如果在设备连接上蓝牙之后,由于某些原因造成与主设备断开连接,会调用如下方法。
与从设备失去连接会触发如下方法。
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
(2)主设备扫描。
- scanForPeripheralsWithServices: options:
扫描到设备,会触发如下代理方法。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
(3)连接设备。
- connectPeripheral:options:
连接设备的结果。会触发如下代理方法。
//连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
//连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
(4)停止扫描。
- stopScan
如果主设备停止扫描外设,没有回调方法。
(5) 获取服务。
- discoverServices:
设备上发现服务,会触发如下代理方法。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
(6)获取特征。
- discoverCharacteristics: forService:
设备获取到特征,会触发如下代理方法。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
(7)发现描述。
- discoverDescriptorsForCharacteristic:
发现了服务,会触发如下代理方法。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
(8)打开通知端口。
- setNotifyValue: forCharacteristic:
通知端口改变,会触发如下代理方法。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
(9)读、写服务数据。
//写数据
- writeValue: forCharacteristic: type:
写入数据后。会触发如下方法。
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
//读数据
- readValueForCharacteristic:
当通知端口为yes,有数据从设备传过来,或者读特此的额时候。这个特征是打开的时候就会触发下面的方法。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
读写描述数据
//写数据
- writeValue:data forDescriptor:
写数据到描述中后会触发如下方法。
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error
//读数据
- readValueForDescriptor:
读取描述数据后会触发如下方法
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error
(10)断开设备。
- cancelPeripheralConnection:
没有回到方法。
(3)Core Bluetooth库介绍
上图是 Core Bluetooth 框架中所有的类。他们的继承关系如下:
CBATTRequest
CBPeripheral : CBPeer
CBCentralManager : CBManager
CBPeripheralManager : CBManager
CBCentral : CBPeer
CBMutableService: CBService : CBAttribute
CBMutableDescriptor: CBDescriptor: CBAttribute
CBMutableCharacteristic: CBCharacteristic: CBAttribute
在下面一篇文章中,我将会对这些类做详细的介绍。包括这个类的作用,所有属性,所有方法。
敬请期待。