蓝牙通用知识概述
发展历史
蓝牙技术是一种近距离无线通信技术,始于爱立信公司1994年的技术方案,在1998年,索尼,英特尔,诺基亚等创立特别兴趣小组(蓝牙技术联盟的前身),负责蓝牙规范的制定和推广
协议栈

- 控制器:底层实现,直接和硬件相关,由芯片厂商实现
- 主机:硬件的抽象。其中通用访问控制配置文件(GAP)。定义了 BLE 整个通信过程中的流程,负责处理设备访问模式和程序,包括设备发现、建立连接、终止连接等等。GAP 层总是作为下面四种角色之一:(1)广播者:不可连接的广播设备。(2)观察者:扫描设备,但不发起建立连接 (3)外部设备:可连接的广播设备,可以在单个链路层连接中作为从机。(4)集中器:扫描广播设备并发起连接,可以在单链路层连接中作为主机
- 应用层:使用主机层提供的API开发应用
BLE应用
- 基于非连接的:不与周边设备发生连接,主要靠扫描到的广播来获取信息。发送广播的一方为广播者(传感器broadcaster),监听广播的一方为观察者(observer),这种方式可以用来实现室内定位。每个广播数据包由31byte组成,
- 基于连接的:两个设备建立GATT连接,形成外围设备(Peripheral)和中心设备(Centeral),一个中心设备可以连接多个外设,但是一个外设只能连接一个中心(外设连接成功后会停止对外广播,因此别人就发现不了它了),一个中心设备的连接外设的数量也是有限的。
蓝牙分类
Android平台提供了Bluetooth API来访问蓝牙功能,可以进行设置蓝牙,查找局部区域内的配对设备,连接设备以及传输数据。
- 传统蓝牙:适用于电池使用强度较大的操作,如设备之间的流传输和通信
- 低功耗蓝牙:Android 4.3 (API 18 )引入了低功耗蓝牙,有些BLE设备一个纽扣电池可以用一两年,因为传输的数据量很少。适用于很多物联网应用场景
- 属性协议(ATT Attribute Protocol):ATT 是整个BLE通信的基础,负责数据封装,使用尽可能少的字节,向外暴露属性的为服务端,获取属性的为客户端。每个属性由通用唯一标识符(UUID)来唯一标识。ATT 传输的属性被格式化为特征 和服务
- 通用属性配置文件(GATT Generic Attribute Profile):是一种传输数据规范,是基于 ATT 做的进一步的逻辑封装,定义数据的交互方式和含义。也被称为 GATT/ATT 。用于在 BLE 链路上发送和接受被称为属性的短数据的通用规范。目前所有低功耗应用配置文件基本都是基于 GATT。GATT 定义了三个非常重要的概念:服务(Service)、特征(Characteristic)、描述(Descripter)。他们的关系如下图
一个 Service 可以包含若干个 Characteristic,一个 Characteristic 可以包含属性(properties)和值(value),还可以包含多个 descripter 。Characteristic 实际上具有读、写、通知等权限。我们在对一个 BLE 设备发起连接成功以后,对他进行读写操作,其实就是对 Characteristic 的操作。图中的 Profile 是一组服务的集合,这些服务组个起来就形成了一个特定的使用场景了,里面的服务是嵌入式工作人员可以添加的。BLE 蓝牙使用 UUID 来区分 Service、Characteristic 、Descripter。 - 特征:征包含单个值和描述特征值的 0 ~ n 个描述符,BLE 设备进行通信的时候主要的操作内容
- 服务:服务中包含一系列的特征值。例如,我们可以使用名为 “心率监测器”的服务,其中包括"心率测量"等特征
蓝牙相关类
蓝牙相关的API在android.bluetooth包中
BluetoothAdapter
本地蓝牙适配器。是所有蓝牙交互的入口,整个系统只有一个蓝牙适配器。
BluetoothDevice
远程的蓝牙设备。利用它可以通过 BluetoothSocket 请求与某个远程设备建立连接和查询设备的信息如名称,地址,绑定状态等
BluetoothSocket
蓝牙套接字接口。类似于TCP Socket,允许应用通过 InputStream 和 OutputStream 与其他蓝牙设备进行数据交换
BluetoothServerSocket
用于监听传入请求的开发服务器套接字。类似于 TCP ServerSocket,要连接两台 Android 设备,其中一台设备必须使用此类开发的一个服务器套接字。当一台远程蓝牙设备向此设备发出连接请求时,BluetoothServerSocket 将会在接受连接后返回已连接的 BluethoothSocket。
BluetoothClass
蓝牙类。描述蓝牙设备的一般特性和功能。这是一组只读属性,用于定义设备的主要和次要设备类及其服务。不过它不能可靠地描述设备支持的所有蓝牙配置文件和服务,而是适合作为设备类型提示
BluetoothProfile
蓝牙配置文件接口。适用于设备间蓝牙通信的无线接口规范。免提配置文件便是一个示例,对于连接到无线耳机的手机,两台设备都必须支持免提配置文件。我们也可以通过实现 BluetoothProfile 接口来写入自己的类来支持特定的蓝牙配置文件。Android API 提供了耳机(BluetoothHeadset),高质量音频(BlutoothA2dp),健康设备(BluetoothHealth)这几种蓝牙配置文件的实现。
BluetoothGatt
低功耗蓝牙通信的配置文件代理。实现了BluetoothProfile
蓝牙开发权限和配置
权限
请求连接、接受连接、和传输数据
<uses-permission android:name = "android.permission.BLUETOOTH"/>
发现或者操作蓝牙设置
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
如果是采用BLE蓝牙,通常还需要声明位置权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
声明支持设备
如果要声明我们的应用仅适用于支持 BLE 的设备,需要清单文件中做如下声明,如果我们希望我们的应用程序在不支持 BLE 的设备上也可以运行的时候,只需要将 true 修改成 false
<uses-feature android:name = "android.hardware.bluetooth_le" android:required = true />
在代码中判断是否支持BLE
if(!getPackageManager().hasSystemFeature(PackgeManger.FEATURE_BLUETOOTH_LE)){
// 不支持 BLE 设备
}
蓝牙使用
设置蓝牙
- 获取蓝牙适配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null){
// 说明此设备不支持蓝牙操作
}
- 启用蓝牙
// 是否开启蓝牙
mBluetoothAdapter.isEnabled()
// 通过系统设置发出启用蓝牙
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent,REQUEST_ENBLE_BT);
// 直接打开蓝牙
mBluetoothAdapter.enable();
查找设备
BluetoothAdapter通过设备发现或查询配对设备的列表来查找远程蓝牙设备。蓝牙设备仅在其当前已启用可检测时才会响应发现请求(返回设备名称,类和mac地址)。利用响应的数据可以与远程设备建立连接,首次建立连接后,将会自动向用户显示配对请求,完成配对后,则会将该设备信息(名称,mac地址)保存在本地(配对设备列表),下次则可以利用本地已知的mac地址发起连接而不需要执行设备发现。
- 发现设备:发现BLE设备和发现普通蓝牙设备方法不一样,并且,只能单独发现普通蓝牙设备或BLE设备,没办法同时执行。发现是一个异步过程,但对于蓝牙适配器来来说会消耗大量资源,所以要确保停止发现后再去连接。
- 可检测性:Android设备默认是处于不可检测状态的,可以发送Intent使系统设置发出可检测模式请求,之后默认设备会变为可检测状态并持续120s,也可以自定义持续时间,最大为3600s。之后将会显示一个用户允许的对话框。若设备未开启蓝牙,启用设备可检测性时会自动启用蓝牙
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
startActivityForResult(discoverableIntent);
- 配对:意味着两台设备知晓彼此的存在,具有可用于身份验证的共享链路密钥,并且能够与彼此建立加密连接。在执行设备发现之前,有必要先查询已配对设备。
- 连接:意味着设备当前共享一个 RFCOMM 通道,并且能够向彼此传输数据
连接设备
两台设备必须实现服务端/客户端机制才能创建连接(一台必须开放服务器套接字,一台必须发起连接)。当服务器和客户端在同一 RFCOMM 通道上分别拥有已连接的 BluetoothSocket 时,二者将被视为彼此连接。在这种情况下每台设备都能获得输入和输出流式传输,并且可以开始传输数据。在连接之前若两个设备未配对,则会自动发出配对请求。
- 服务器设备必须保持开放一个BluetoothServerSocket,监听传入的连接请求,在接受请求后从BluetoothServerSocket提供一个已经连接的BluetoothSocket,然后就可以close这个监听,并释放BluetoothServerSocket的所有资源,但不会关闭已经连接的BluetoothSocket,与TCP/IP不同的是RFCOMM 一次只允许每个通道有一个已经连接的客户端
- 客户端设备通过connect发起连接,系统将会在远程设备上执行 SDP 查找,来匹配 UUID。如果查找成功了并且远程设备接受了该连接,它将共享 RFCOMM 通道在连接期间使用。这个时候 connect() 就会返回。这个方法也是阻塞的,如果失败或者超时(12秒之后),将引发异常。调用 connect 的时候要确保客户端没有执行发现操作。如果执行了会大幅度降低连接的速度,增加失败的可能性,因此在连接之前,应该尽量都要调用一次cancelDiscovery(无论是否在扫描)
- 建立连接后,两个设备都有一个BluetoothSocket,并通过该Socket进行流式传输数据4
- 与BLE设备连接,就是连接到该设备的GATT服务,并得到一个BluetoothGatt对象,通过该对象和BLE设备交互,在支持的位置读取和写入属性。