2、蓝牙基础概念:经典蓝牙与低功耗蓝牙(BLE)的区别、蓝牙协议栈、GAP/GATT/ATT/SMP等核心协议
在开始Android蓝牙服务项目开发之前,必须先厘清蓝牙技术体系中的核心概念。很多初学者在编写代码时,往往因为混淆了经典蓝牙(BR/EDR)与低功耗蓝牙(BLE)的API调用方式、连接模型和数据传输机制,导致项目反复返工。本章将从物理层差异、协议栈分层、核心协议(GAP、GATT、ATT、SMP)三个维度,帮助你建立完整的蓝牙知识框架,为后续的Android蓝牙实战打下坚实基础。
2.1 经典蓝牙与低功耗蓝牙(BLE)的本质差异
核心原理:经典蓝牙(BR/EDR,Basic Rate/Enhanced Data Rate)与低功耗蓝牙(BLE,Bluetooth Low Energy)虽然共享2.4GHz ISM频段,但它们在设计哲学上截然不同。经典蓝牙从诞生之初就面向持续、稳定的数据流传输,例如无线耳机、车载免提、文件传输等场景,其协议栈设计强调高吞吐量和低延迟,但代价是功耗较高。而BLE则面向间歇性、小数据量的物联网场景,如智能手环、传感器节点、信标等,其核心设计目标是“用尽可能少的能量完成一次数据交换”。
从物理层看,经典蓝牙使用79个信道(每个1MHz带宽),采用1600跳/秒的跳频机制;BLE则只使用40个信道(每个2MHz带宽),其中3个为广播信道(37/38/39),37个为数据信道。BLE的跳频速率较低,但通过更窄的接收窗口和更短的唤醒时间实现省电。在传输速率上,经典蓝牙的EDR模式可达2-3Mbps,而BLE 5.0的2M PHY模式最高也只有2Mbps,实际有效吞吐量通常在1.3Mbps左右。
| 对比维度 | 经典蓝牙(BR/EDR) | 低功耗蓝牙(BLE) |
|---|---|---|
| 信道数量 | 79个(1MHz间隔) | 40个(2MHz间隔,3个广播信道) |
| 峰值速率 | 2-3Mbps(EDR) | 1Mbps(LE 1M)/ 2Mbps(LE 2M) |
| 连接建立时间 | 约100ms(需查询+寻呼) | 约3-10ms(广播+扫描) |
| 功耗特性 | 持续传输时功耗约30-50mA | 峰值电流约15mA,平均可低至μA级 |
| 数据交互模型 | 面向流(Stream),RFCOMM串口、A2DP音频 | 面向属性(Attribute),GATT服务/特征 |
| 典型应用 | 蓝牙耳机、音箱、车载、文件传输 | 手环、Beacon、传感器、智能家居 |
| Android API | BluetoothSocket / BluetoothServerSocket | BluetoothGatt / BluetoothGattCallback |
判断方法:在Android项目中,可以通过 BluetoothDevice.getType() 方法判断设备类型:返回 DEVICE_TYPE_CLASSIC(经典)、DEVICE_TYPE_LE(低功耗)或 DEVICE_TYPE_DUAL(双模)。双模设备(如大多数现代手机)同时支持两种协议,但Android系统会为每种协议提供独立的API入口。
实战场景:假设你正在开发一个运动手环App,需要实时接收心率数据(每秒钟1次,每次2字节),同时需要与手机进行固件升级(传输约500KB的固件包)。此时应选择BLE进行心率数据传输(低功耗、小数据量),而固件升级则可以考虑使用经典蓝牙的RFCOMM通道(更高的吞吐量,减少升级等待时间)。如果强行使用BLE传输500KB固件,虽然可行,但受限于MTU(最大传输单元,默认23字节,协商后可达247字节),需要大量分包和确认,升级时间会显著增加。
常见误区:很多开发者误以为“BLE就是蓝牙4.0及以上版本的统称”,实际上蓝牙4.0引入了BLE,但蓝牙4.0/5.0设备仍然可以支持经典蓝牙。Android的 BluetoothAdapter 同时管理两种协议,但 startDiscovery() 只能发现经典蓝牙设备,BLE设备需要使用 startLeScan() 或 BluetoothLeScanner。混淆这两套API是项目中最常见的错误之一。
2.2 蓝牙协议栈分层架构
核心原理:蓝牙协议栈是一套分层的软件架构,每一层负责特定的功能,并为上层提供抽象接口。理解协议栈的分层有助于定位问题——当数据交互失败时,你需要判断是物理层信号问题、链路层连接问题,还是应用层协议配置问题。Android系统通过BlueZ(早期)或Fluoride(当前)蓝牙协议栈实现,但应用开发者通常只接触最上层的API。
协议栈从下到上依次为:物理层(PHY)负责射频信号的调制解调;链路层(LL)负责数据包的封装、重传、加密和连接状态管理;L2CAP层提供逻辑信道的复用和分段重组;应用层则根据具体协议(RFCOMM、GATT等)提供数据交互接口。对于BLE而言,链路层之上直接承载ATT和SMP协议,而经典蓝牙则在L2CAP之上运行RFCOMM、SDP等。
// Android中查看蓝牙协议栈版本(API 30+)
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
int supportedProfiles = adapter.getSupportedProfiles();
// 检查是否支持BLE
if (adapter.isLeSupported()) {
Log.d("Bluetooth", "BLE supported");
}
// 检查是否支持经典蓝牙
if (adapter.isClassicSupported()) {
Log.d("Bluetooth", "Classic supported");
}
}
操作流程:在Android开发中,你不需要直接操作协议栈底层,但需要理解API调用与协议层的对应关系。例如,调用 BluetoothGatt.connect() 时,系统会依次执行:链路层建立连接(LL Connection)、L2CAP信道建立、ATT协议握手、GATT服务发现。如果连接失败,你可以通过 onConnectionStateChange 回调中的状态码判断失败原因——状态133表示连接被远端拒绝,状态133则可能是链路层超时。
经验提醒:在调试BLE连接问题时,建议开启Android的蓝牙HCI日志(开发者选项 → 开启蓝牙HCI信息收集日志),然后使用Wireshark分析btsnoop_hci.log文件。这能让你看到完整的链路层数据包交换过程,快速定位是广播问题、扫描问题还是连接参数协商失败。
2.3 GAP:通用访问协议
核心原理:GAP(Generic Access Profile)定义了蓝牙设备如何被发现、如何建立连接以及如何管理连接参数。它是所有蓝牙应用的基础,无论是经典蓝牙还是BLE都必须遵循GAP规范。GAP定义了四种角色:广播者(Broadcaster)、观察者(Observer)、外围设备(Peripheral)和中心设备(Central)。在BLE中,外围设备(如手环)发送广播包,中心设备(如手机)扫描并主动发起连接。
GAP还定义了连接参数的协商机制,包括连接间隔(Connection Interval)、从机延迟(Slave Latency)和超时时间(Supervision Timeout)。这些参数直接影响功耗和响应速度。例如,连接间隔设置为30ms时,设备每30ms唤醒一次接收数据;设置为100ms时,功耗更低但数据延迟增加。Android中可以通过 requestConnectionPriority() 方法请求调整连接参数,但最终决定权在从机设备。
| GAP角色 | Android API | 典型设备 | 职责 |
|---|---|---|---|
| 广播者 | AdvertiseCallback | Beacon、传感器 | 周期性发送广播包,不建立连接 |
| 观察者 | ScanCallback | 扫描器、手机 | 监听广播包,不发起连接 |
| 外围设备 | BluetoothGattServer | 手环、心率带 | 接受连接,提供数据服务 |
| 中心设备 | BluetoothGatt | 手机、平板 | 发起连接,读取/写入数据 |
案例拆解:假设你正在开发一个智能门锁App,门锁作为外围设备广播“SmartLock_1234”的广播包,广播数据中包含服务UUID(0xFFE0)和自定义厂商数据(用于标识门锁状态)。手机作为中心设备扫描到该广播后,解析广播包中的服务UUID,确认是目标设备后发起连接。连接建立后,门锁通过GATT服务向手机发送开锁状态通知。整个过程中,GAP负责设备发现和连接建立,而GATT负责后续的数据交互。
常见误区:广播包的最大长度是31字节(传统广播)或255字节(扩展广播),很多开发者试图在广播包中塞入大量业务数据,导致广播包被截断或无法被扫描到。广播包应只包含必要的标识信息(设备名、服务UUID、少量状态标志),详细数据应通过连接后的GATT服务获取。
2.4 ATT与GATT:属性协议与通用属性规范
核心原理:ATT(Attribute Protocol)是BLE的数据传输基础,它定义了一种基于属性的数据交换模型。每个属性由句柄(Handle)、类型(UUID)、权限(Permissions)和值(Value)组成。GATT(Generic Attribute Profile)则建立在ATT之上,定义了属性如何组织成服务(Service)和特征(Characteristic),以及客户端如何发现和操作这些服务。
在GATT模型中,数据以层级结构组织:服务(Service)包含多个特征(Characteristic),每个特征包含一个值(Value)和若干描述符(Descriptor)。例如,心率服务(0x180D)包含心率测量特征(0x2A37),该特征的值是心率数据,同时包含一个客户端特征配置描述符(CCCD,0x2902),用于控制是否启用通知功能。
// Android中操作GATT服务
BluetoothGatt gatt = device.connectGatt(context, false, callback);
// 在onServicesDiscovered回调中获取服务
BluetoothGattService heartRateService = gatt.getService(UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb"));
BluetoothGattCharacteristic heartRateChar = heartRateService.getCharacteristic(UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb"));
// 启用通知
gatt.setCharacteristicNotification(heartRateChar, true);
BluetoothGattDescriptor cccd = heartRateChar.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
cccd.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(cccd);
操作流程:GATT客户端(手机)与服务器(外设)的交互遵循严格的流程:①连接建立后,客户端发起服务发现(Discover Services);②服务器返回服务及其特征列表;③客户端根据需要读取特征值、写入特征值或订阅通知;④服务器通过通知(Notification)或指示(Indication)主动推送数据。ATT协议定义了六种PDU(协议数据单元):请求、响应、命令、通知、指示、确认。
判断方法:当需要判断一个BLE设备是否支持某个功能时,应检查其GATT服务列表。例如,如果设备支持电量显示,则必须包含电池服务(0x180F);如果支持设备信息,则包含设备信息服务(0x180A)。Android中可以通过 gatt.getServices() 获取所有服务,然后遍历查找目标UUID。
实战场景:以智能温度计为例,温度计作为GATT服务器,提供温度服务(自定义UUID:0000AAA0-0000-1000-8000-00805F9B34FB),包含温度特征(0000AAA1-...)和配置特征(0000AAA2-...)。手机连接后,先发现服务,然后读取温度特征获取当前温度,同时写入配置特征启用温度变化通知。当温度变化超过0.5℃时,温度计通过Notification推送新数据。整个交互过程数据量小、延迟低,非常适合BLE。
2.5 SMP:安全管理协议
核心原理:SMP(Security Manager Protocol)负责BLE的配对(Pairing)、认证(Authentication)和加密(Encryption)。BLE的配对过程分为三个阶段:①配对特征交换(Pairing Feature Exchange),双方交换I/O能力、认证要求等;②短期密钥(STK)生成,使用LE Legacy Pairing或LE Secure Connections(基于ECDH P-256椭圆曲线);③传输特定密钥分发,包括长期密钥(LTK)、身份解析密钥(IRK)等。
Android中,配对过程通常由系统自动处理,但开发者需要理解配对模式对用户体验的影响。例如,如果外设没有显示屏和键盘(如传感器),应选择“Just Works”配对模式(无需用户交互);如果外设有显示屏,可以选择“Numeric Comparison”模式(显示6位数字确认)。Android的 BluetoothDevice.createBond() 方法可以触发配对,但配对对话框由系统UI控制。
| 配对模式 | I/O能力要求 | 安全级别 | 典型场景 |
|---|---|---|---|
| Just Works | 无 | 低(易受中间人攻击) | 传感器、Beacon |
| Numeric Comparison | 显示屏+确认按钮 | 高(防MITM) | 智能门锁、手环 |
| Passkey Entry | 键盘输入 | 高(防MITM) | 键盘、遥控器 |
| Out of Band | NFC或近场通信 | 最高 | 支付设备、门禁 |
案例拆解:假设你正在开发一个医疗级血压计App,血压计支持BLE连接并需要传输敏感的生理数据。根据医疗数据合规要求,必须启用加密连接。在Android中,你需要在连接后检查 BluetoothGatt.getDevice().getBondState(),如果未配对则调用 createBond()。配对完成后,系统会自动加密链路。但需要注意,Android的配对对话框是系统级的,你无法自定义UI,只能通过 BroadcastReceiver 监听 ACTION_BOND_STATE_CHANGED 来感知配对状态变化。
常见误区:很多开发者认为“配对”和“连接”是同一回事。实际上,配对是安全凭证的交换过程,连接是数据传输链路的建立过程。一个已配对的设备可以断开连接,下次重新连接时无需再次配对。在Android中,已配对的设备会出现在系统蓝牙设置列表中,但你的App需要通过 BluetoothAdapter.getBondedDevices() 获取已配对设备列表。
总结复盘:本章的核心要点可以归纳为:①经典蓝牙与BLE在物理层、协议栈、API调用上存在本质差异,项目选型时必须明确需求;②协议栈分层是理解蓝牙问题的钥匙,从物理层到应用层逐层排查;③GAP负责“找到设备并建立连接”,GATT/ATT负责“连接后如何交换数据”,SMP负责“如何安全地交换数据”;④Android开发中,经典蓝牙使用Socket API,BLE使用Gatt API,两者不可混用。在实际项目中,建议先画出协议栈分层图,标注出你的应用涉及哪些层,再开始编码。