做 ble 的时候,很多人第一次看到 notify 和 indicate,会觉得它们看起来差不多。
表面上也确实差不多。两者都是设备主动往手机推数据,android 端通常都会在 oncharacteristicchanged() 里收到回调。你如果只看上层现象,很容易把它们理解成“两个名字不同的通知模式”。
但真写项目,尤其是设备协议稍微复杂一点以后,notify 和 indicate 的区别并不只是名词差异。它们背后对应的是两种不同的可靠性模型,而这个差异会直接影响你的收消息稳定性、吞吐量、延迟,甚至会影响你到底该怎么设计协议。
所以这篇不讲太泛的 ble 概念,就讲一个实际问题:notify 和 indicate 到底差在哪,android 端该怎么理解,项目里又该怎么选。
先说结论
如果只压成一句话:
notify更快,更轻,但不保证每条都被确认indicate更稳,更重,每一条都需要对端确认
这句话基本对,但还不够你写项目。
因为 android 开发里最容易误判的地方,不是“不知道它们有区别”,而是不知道这个区别会影响协议设计。
从 ble 协议语义看
ble gatt 里,characteristic 的值变化可以通过 server 主动推送给 client。这里主要有两种方式:
- notification
- indication
notification 的特点是 server 发出去就发出去了,不要求 client 回一个链路层确认。indication 的特点是 server 发出去以后,要等 client 确认,这条链路才算完成。
也就是说,indicate 不是比 notify “高级一点”,而是它本身就多了一层确认语义。
这个差异非常关键。
因为它意味着:
notify更适合高频状态流indicate更适合关键状态或关键事件
如果你把高频数据流全做成 indicate,链路会被确认机制拖慢。
如果你把关键确认消息全做成 notify,链路又可能在边界场景下不够稳。
android 端为什么看起来差不多
很多人会混淆,是因为 android 端最后收消息的回调长得一样:
override fun oncharacteristicchanged(
gatt: bluetoothgatt,
characteristic: bluetoothgattcharacteristic,
value: bytearray
) {
log.d("ble", "received=${value.jointostring(" ") { "%02x".format(it) }}")
}不管底层是 notify 还是 indicate,最终都可能进这个回调。
所以如果你只从 android api 表层去看,会觉得它们没有本质区别。
但这只是因为 android 把“收到值变化”统一封装成了同一个回调,不代表底层行为一样。
真正的差异发生在设备侧和链路语义上,而不是发生在你收到回调的那一刻。
打开 notify 和 indicate,代码为什么也很像
在 android 端,开启两者的流程也非常接近。通常都是两步:
- 本地调用
setcharacteristicnotification() - 写
cccddescriptor
关键差别在于你给 cccd 写的值不同。
开启 notify
val characteristic = service.getcharacteristic(notify_uuid)
gatt.setcharacteristicnotification(characteristic, true)
val cccd = characteristic.getdescriptor(
uuid.fromstring("00002902-0000-1000-8000-00805f9b34fb")
)
cccd.value = bluetoothgattdescriptor.enable_notification_value
gatt.writedescriptor(cccd)开启 indicate
val characteristic = service.getcharacteristic(indicate_uuid)
gatt.setcharacteristicnotification(characteristic, true)
val cccd = characteristic.getdescriptor(
uuid.fromstring("00002902-0000-1000-8000-00805f9b34fb")
)
cccd.value = bluetoothgattdescriptor.enable_indication_value
gatt.writedescriptor(cccd)真正的区别就在:
enable_notification_valueenable_indication_value
也就是说,android 端不是通过不同回调区分,而是通过写不同的 descriptor 值去告诉设备:你应该用哪种方式推数据。
什么时候更适合用 notify
notify 更适合这些场景:
- 高频传感器数据
- 实时状态流
- 音频或流式数据
- 变化很快、允许丢个别包的场景
原因很简单,它轻。
没有每条都要确认的开销,设备可以更快地往上推。
所以如果你有一个设备每隔几十毫秒就上报一次状态,或者持续推一段连续数据,通常更适合 notify。
比如耳机、电量变化、佩戴状态流、某些实时遥测数据,这类都更适合用 notify。
但它的问题也很明确:如果你把所有东西都丢给 notify,你就默认接受一个前提,链路不会帮你逐条确认。
所以 notify 适合“多、快、可持续”的数据,不适合“必须一条不丢”的关键控制确认。
什么时候更适合用 indicate
indicate 更适合这些场景:
- 关键状态变更确认
- 必须可靠到达的结果回包
- 低频但重要的控制响应
- 某些升级、配对、鉴权类消息
因为 indicate 的特点不是快,而是它自带确认语义。
如果设备要告诉 app 一个特别关键的状态,比如:
- 配置写入成功
- 某个模式切换已完成
- 升级状态切换
- 某一步校验通过
这种时候 indicate 往往比 notify 更合理。
因为这类消息一旦漏掉,app 侧状态机就可能直接走歪。
真正的区别不是“哪个更好”,而是“你在传什么”
很多 ble 项目写得不稳,本质上不是不会调 api,而是没有把数据按语义分层。
比如把高频状态流做成 indicate,结果整条链路被确认机制拖得很慢。
或者把关键确认消息做成 notify,结果某次状态切换没收到,app 侧以为设备没响应。
这就是为什么 notify 和 indicate 的区别不能只停在一句“一个快一个稳”。
更准确一点的说法应该是:
notify面向数据流indicate面向关键状态
如果你把这个边界想清楚,协议设计会稳很多。
android 端最容易踩的坑
1. 以为setcharacteristicnotification()就够了
很多人这样写:
gatt.setcharacteristicnotification(characteristic, true)
然后发现死活收不到数据。
原因通常不是 notify 和 indicate 的区别,而是你根本没写 cccd。
这一步不做,设备侧很多时候不会真正开始推送。
2. 写错了 descriptor 值
如果设备 characteristic 支持的是 indicate,你却写了 enable_notification_value,那链路很可能就不工作。
反过来也一样。
所以这件事不能凭感觉,必须看设备协议文档和 characteristic property。
3. 不看 characteristic 的 property
不是所有 characteristic 都同时支持 notify 和 indicate。
在 android 端最好先检查一下:
val properties = characteristic.properties
val supportsnotify =
properties and bluetoothgattcharacteristic.property_notify != 0
val supportsindicate =
properties and bluetoothgattcharacteristic.property_indicate != 0如果设备只支持其中一种,你写另一种 descriptor 值,本来就不对。
4. 以为收不到数据一定是 android 的问题
很多时候 android 端代码都写对了,但还是没数据。
这时候问题可能在设备侧:
- 设备没真正开启推送
- 设备需要先发初始化命令
- 设备只有在某种模式下才会上报
- 设备协议里 notify/indicate 的使用条件有前置状态
也就是说,oncharacteristicchanged() 没回调,不等于一定是手机问题。
在协议设计里怎么选
如果你自己能参与设备协议设计,我建议按这个思路来分:
高频、连续、允许局部丢失的数据,用 notify。
关键、低频、必须确认的状态,用 indicate。
比如:
- 设备实时传感器流,优先
notify - anc 模式切换结果,优先
indicate - 耳机状态周期性同步,优先
notify - ota 某一步完成确认,优先
indicate
这样协议语义会更清晰,android 端也更容易围绕不同消息建立状态机。
项目里最实用的一句判断
如果你现在在做 ble 项目,遇到“为什么有些消息总感觉不稳”,最先该问自己的不是:
“android 这个回调是不是有 bug?”
而是:
“这条消息本来就应该用 notify,还是 indicate?”
因为很多收消息问题,根源不是 api 没调对,而是协议语义一开始就选错了。
结尾
notify 和 indicate 在 android 端看起来很像,都是通过 characteristic 变化把数据推上来。但真正的差异不在回调,而在底层的确认语义。
notify 更适合快一点、连续一点的数据。indicate 更适合慢一点、但必须稳一点的关键状态。
所以这个问题最后其实不是“它们有什么区别”,而是:
你这条消息,到底是在传数据流,还是在传一个不能丢的状态。
到此这篇关于android ble 的 notify 和 indicate 到底有什么区别的文章就介绍到这了,更多相关android ble notify 和 indicate 区别内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论