直接看图。
从图中可以看到,对于数据量较大的传输,如音视频等开发,需要使用经典蓝牙模式。而对于现在兴起的 AIOT 物联网、智能家居等领域,则会选择低功耗蓝牙 BLE,因为成本和功耗更低,并对实时性要求更高。
从现如今手机的实际体验来看,手机上的蓝牙应该是双模蓝牙,以小米手机为例,它可以连接蓝牙耳机进行听音乐,也可以连接智能家居设备,控制家电。
所以,根据上图划分,按照应用划分的话,基本可以确定哪类场景使用哪种开发方式。具体不再赘述。
如果直接想看代码, 可以直接看这里:
那么,低功耗蓝牙和经典蓝牙的区别究竟在哪里呢?
要是仅仅从两者的通信方式上来说,可以说除了名字叫蓝牙外,完全可以当做两个东西。这也就为很多搞过经典蓝牙以为就可以很轻松的接着搞低功耗蓝牙的人埋下了一个大坑。
不过,两者在总体上的流程却也是相似的(好吧,无线通信貌似也都是这么个流程),那就是:
发现设备->配对/绑定设备->建立连接->数据通信
经典蓝牙和低功耗蓝牙除了配对/绑定这个环节是一样的之外,其它三个环节都是不同的。
经典蓝牙设备发现其它经典蓝牙设备的方式是调用BluetoothAdapter的startDiscovery()方法,这个方法只能够发现经典蓝牙设备。
低功耗蓝牙中则有一个主设备(Central)和从设备(Peripheral,也叫外围设备)的概念。主设备作为发现方,调用发现设备的方法,通过BluetoothAdapter的startLeScan()方法实现。从设备则作为被发现方,发出广播,以供发现。同样,这个startLeScan()方法也仅能够发现低功耗蓝牙从设备。
不过,在Android系统蓝牙搜索界面,两种蓝牙设备都是可以被发现的。只有当两种蓝牙设备被某设备(包括当前的设备)配对/绑定后,才不会再被扫描到。
有很多小伙伴都不太理解配对和绑定究竟有什么区别,或者它们根本就是同一个东西。好吧,严格说配对和绑定是有区别的,也就是不是指的同一件事情。但是这两者的区别比较模糊,也不好解释。目前JACK的机器人的理解是,配对是建立两者的对应关系,而绑定则把这层关系保存固定下来并进行了强化,暂时这么理解着吧。
不管是经典蓝牙还是低功耗蓝牙,绑定方法都是通用的,可以调用相同的绑定方法。
在建立连接的方式上,两者就千差万别了。
在蓝牙设备中,存在着物理地址,我们也叫作蓝牙的 MAC 地址,这个地址是唯一的,就像咱们网络上的 IP地址。同时还存在着一个叫做 UUID 的东西,可以把它理解为是 IP 地址中的端口号。正如知道了 IP 地址和端口号,就知道了怎么链接到目标网络服务器位置,知道了蓝牙设备的 MAC 地址和 UUID 也就能够确定到具体是哪一台蓝牙设备了,这两者合起来就是蓝牙的唯一身份标识。
经典蓝牙建立连接的方式实际上就是 Socket 的连接的建立。
只不过这里不是直接用 Socket,而是 BluetoothSocket。获取 BluetoothSocket 的方式也很简单,利用搜索找到的BluetoothDevice,调用其方法 createRfcommSocketToServiceRecord(UUID)。最后,使用获取到的BluetoothDevice 调用其方法 connect() 就建立了经典蓝牙设备之间的连接通道。
低功耗蓝牙则用了一种看起来比较怪异的方式建立连接。我们先来看关于 BLE 的一些基本概念:
Generic Attribute Profile (GATT)
通过BLE连接,读写属性类小数据的 Profile 通用规范。现在所有的 BLE 应用 Profile 都是基于 GATT 的。
Attribute Protocol (ATT)
GATT 是基于 ATT Protocol 的。ATT 针对 BLE 设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的 UUID,属性将以 characteristics and services 的形式传输。
Characteristic
Characteristic 可以理解为一个数据类型,它包括一个 value 和 0 至多个对次 value 的描述(Descriptor)。
Descriptor
对 Characteristic 的描述,例如范围、计量单位等。
Service
Characteristic 的集合。例如一个 service 叫做 “Heart Rate Monitor”,它可能包含多个 Characteristics,其中可能包含一个叫做 “heart rate measurement" 的 Characteristic。
这里举个例子,例如现在需要使用一个智能手机作为主设备去连接一个作为从设备的智能手环,那么,此时这个作为主设备的智能手机连接过程中实际是一个客户端(Client),而作为从设备的智能手环在此过程中则是服务端(Server)。这里的主设备和从设备,客户端和服务端一定要区分清楚。
想要和一台 BLE 从设备建立连接,一般是某个智能设备,例如智能手环、智能灯泡之类的。如果使用智能手机作为测试平台,其硬件条件是,蓝牙得至少是低功耗蓝牙版本,然后安卓系统的话,至少得是 Android 4.3以上系统才行,因为 Google 在 Android 4.3 以上才做了BLE主设备的支持,如果想将智能手机作为 BLE 从设备,则必须在 Android 5.0 以上才行。
具体建立 GATT 连接的顺序则是,首先通过 BluetoothAdapter 的 getRemoteDevice(address) 方法获取到相应 BLE 从设备的 BluetoothDevice,其中的 address 为目标蓝牙设备 MAC 地址。
然后通过此BluetoothDevice的connectGatt(this, false, mGattCallback)方法获取设备连接。此时的连接,只能够进行监听,也就是获取到当前 BLE 从设备广播出来的数据。
经典蓝牙中,当建立连接后,就可以直接使用 BluetoothSocket 的 getOutputStream() 方法获取输出流写入需要发送的数据。读取发送回来的数据,则是调用 BluetoothSocket 的 getInputStream() 方法获取输入流读取。这点和 Java中 的 Socket 通信几乎是一模一样。
而在低功耗蓝牙中,想要实现主设备对从设备的数据发送,则需要直接读取获取到的从设备的Characteristic,而Characteristic 又是 Service 下面的一层,所以操作顺序是:
通过 BLE 从设备相应的 Service_UUID 获取对应的 BluetoothGattService,获取方法是:使用BluetoothDevice 的 connectGatt(this, false, mGattCallback) 方法返回的 BluetoothGatt 对象,调用BluetoothGatt 的方法 getService(Service_UUID) 获取相应的BluetoothGattService;
调用 BluetoothGattService 和对应的 Characteristic 的写入 UUID 获取相应的BluetoothGattCharacteristic,获取方法是:调用 BluetoothGattService 的getCharacteristic(Characteristic_UUID) 方法获得;
设置需要发送的命令值,调用 BluetoothGattCharacteristic 的方法 setValue(value) 进行设置,其中 value一般为 byte[];
最后,使用 BluetoothGatt 的写入方法 writeCharacteristic(TxChar) 完成命令发送。
可以看到,想要实现 BLE 的数据通信,步骤相当繁琐,这里只是做一个简单的概念理解,如果想要获取到BLE 从设备的返回值,还需要设置 Notification,然后调用 BluetoothGatt 的 readCharacteristic(characteristic) 方法进行数据的读取,这里不做详细说明了,放在以后详细开发 BLE 蓝牙通信的时候再做解释。