Flutter for ble 之set_notification_error, could not locate CCCD descriptor for characteristic分析(原生角度)
一、背景
本文主要给前端同学提供建议,对于android的原生开发者可能过于简单哦。
库:flutter_blue
flutter下使用该库开发ble应用,在调用如下方法时:
await mCharacteristic.setNotifyValue(true);
出现:
E/flutter (18304): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(set_notification_error, could not locate CCCD descriptor for characteristic: xxxxx, null)
E/flutter (18304): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
E/flutter (18304): #1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:321:33)
二、分析
我们可以从log的报错信息:could not locate CCCD descriptor for characteristic 中可以看出报错的原因是没有找到cccd的描述符。
首先我们在github的Issues中找找,找到如下的修复信息:
chaochaox1990 commented on 27 Dec 2019@chaochaox1990 same error, have you fixed it?yes I already fixed. All I do just change the value of CCCD.
change the value of CCCD!
那么CCCD是什么的,这里博主给大家介绍一下。在Android中的GATT中的可Notify的characteristic中,有个特征值的描述Descriptor(uuid:00002902-0000-1000-8000-00805f9b34fb),这个值由蓝牙硬件出厂的时候写入,主要作用就是characteristic的权限控制,例如,是否开启等。在Android中的构造方法如下:
public BluetoothGattDescriptor(UUID uuid, int permissions) {initDescriptor(null, uuid, 0, permissions);}
一般来说uuid如上,是固定的,参数为其读写等权限。BluetoothGattDescriptor详细介绍
那么为什么插件会报这个错呢,我们进入其源码看一下,在bluetooth_characteristic.dart文件中,可以看到报错代码如下:
await FlutterBlue.instance._channel.invokeMethod('setNotification', request.writeToBuffer());
这里还是看不出端倪来,那我们去github项目中的源码看一看,这里它调用的是setNotification这个方法。我们来到这个路径:android/src/main/java/com/pauldemarco/flutter_blue/FlutterBluePlugin.java,查看这部分代码:
case "setNotification":{byte[] data = call.arguments();Protos.SetNotificationRequest request;try {request = Protos.SetNotificationRequest.newBuilder().mergeFrom(data).build();} catch (InvalidProtocolBufferException e) {result.error("RuntimeException", e.getMessage(), e);break;}BluetoothGatt gattServer;BluetoothGattCharacteristic characteristic;BluetoothGattDescriptor cccDescriptor;try {gattServer = locateGatt(request.getRemoteId());characteristic = locateCharacteristic(gattServer, request.getServiceUuid(), request.getSecondaryServiceUuid(), request.getCharacteristicUuid());cccDescriptor = characteristic.getDescriptor(CCCD_ID);if(cccDescriptor == null) {throw new Exception("could not locate CCCD descriptor for characteristic: " +characteristic.getUuid().toString());}} catch(Exception e) {result.error("set_notification_error", e.getMessage(), null);return;}byte[] value = null;if(request.getEnable()) {boolean canNotify = (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0;boolean canIndicate = (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0;if(!canIndicate && !canNotify) {result.error("set_notification_error", "the characteristic cannot notify or indicate", null);return;}if(canIndicate) {value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;}if(canNotify) {value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;}} else {value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;}if(!gattServer.setCharacteristicNotification(characteristic, request.getEnable())){result.error("set_notification_error", "could not set characteristic notifications to :" + request.getEnable(), null);return;}if(!cccDescriptor.setValue(value)) {result.error("set_notification_error", "error when setting the descriptor value to: " + value, null);return;}if(!gattServer.writeDescriptor(cccDescriptor)) {result.error("set_notification_error", "error when writing the descriptor", null);return;}result.success(null);break;}
这部分就是在Android环境下执行的代码。从:
cccDescriptor = characteristic.getDescriptor(CCCD_ID);if(cccDescriptor == null) {throw new Exception("could not locate CCCD descriptor for characteristic: " +characteristic.getUuid().toString());}
static final private UUID CCCD_ID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
可以看出,只要characteristic中没有uuid为"00002902-0000-1000-8000-00805f9b34fb"的descriptor,就会报错,下面notify的关键代码没有执行,因此notify失败。
gattServer.setCharacteristicNotification(characteristic, request.getEnable())
这看似苛刻,其实这就是标准。可以notify的characteristic必须要有uuid为:"00002902-0000-1000-8000-00805f9b34fb"的descriptor。很多硬件都会有这个问题呢?造成这样的原因就是国内蓝牙厂商开发标准参差不齐,要不是没有descriptor,就是descriptor的uuid不是标准的。那么为什么他们不统一标准的,因为在原生开发中,我们一般不这样苛刻,下面给大家看看我们原生一般怎么写的:
String bluetoothGattInfoKey = entry.getKey();BluetoothGattChannel bluetoothGattInfoValue = entry.getValue();if (bluetoothGatt != null && bluetoothGattInfoValue.getCharacteristic() != null) {BreoLog.d("enable-" + bluetoothGattInfoValue.getCharacteristic().getUuid().toString());success = bluetoothGatt.setCharacteristicNotification(bluetoothGattInfoValue.getCharacteristic(), enable);}BluetoothGattDescriptor bluetoothGattDescriptor = null;if (bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() != null) {bluetoothGattDescriptor = bluetoothGattInfoValue.getDescriptor();} else if (bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() == null) {if (bluetoothGattInfoValue.getCharacteristic().getDescriptors() != null&& bluetoothGattInfoValue.getCharacteristic().getDescriptors().size() == 1) {bluetoothGattDescriptor = bluetoothGattInfoValue.getCharacteristic().getDescriptors().get(0);} else {bluetoothGattDescriptor = bluetoothGattInfoValue.getCharacteristic().getDescriptor(UUID.fromString(BleConstant.CLIENT_CHARACTERISTIC_CONFIG));}}if (bluetoothGattDescriptor != null) {bluetoothGattInfoValue.setDescriptor(bluetoothGattDescriptor);if (isIndication) {if (enable) {bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);} else {bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);}} else {if (enable) {bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);} else {bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);}}if (bluetoothGatt != null) {bluetoothGatt.writeDescriptor(bluetoothGattDescriptor);}}
看吧,这两步操作是并行的,有的话就设置一下,没有也很佛系的。
这里给大家看两张图,图一的就是不正常的,characteristic是支持notify的,但是没有descriptor。图二就是正常的,有descriptor。
三、解决方案
1、让ble模块的供应商把descriptor写进去(推荐)。
2、修改一下flutter_blue插件,不让它那么苛刻严格。
3、试一试其他插件。pub仓库
Flutter for ble 之set_notification_error, could not locate CCCD descriptor for characteristic分析(原生角度)相关推荐
- 解决在终端使用“flutter doctor”命令后报:“Unable to locate Andro“错误 以及android sdk目录中找不到tools/bin目录解决
"Unable to locate Andro"错误说明电脑没有配置Android sdk环境变量 直接先上解决办法: 1.在系统变量中添加该变量:(如下图) 在Path中新建: ...
- CSR867x — IOS设备搜索到的BLE外设名称与实际名称不相符的问题分析
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX 作 者:文化人 XX 联系方式:(或进群:471144274联系我) ...
- Flutter瘦身大作战
作者:闲鱼技术-三莅 背景 闲鱼技术团队于2018年上半年率先引入了Flutter技术实现客户端开发,到目前为止成功改造并上线了复杂的商品详情和发布业务.随着改造业务的增多,安装包体积急剧上增.安装包 ...
- flutter 应用场景_Flutter混合开发的路由栈管理
为了把 Flutter 引入到原生工程,我们需要把 Flutter 工程改造为原生工程的一个组件依赖,并以组件化的方式管理不同平台的 Flutter 构建产物,即 Android 平台使用 aar.i ...
- Google跨平台UI框架 Flutter beta 重磅发布,这里有你需要了解的一切
2018年2月27日,在西班牙巴塞罗那世界移动大会上,Google发布 Flutter 的第一个 beta 版本.Flutter 是 Google 用以帮助开发者在 iOS 和 Android 两个平 ...
- 关于Flutter初始化流程,我必须告诉你的是...
作者:闲鱼技术-然道 1. 引言 最近在做性能优化的时候发现,在混合栈开发中,第一次启动Flutter页面的耗时总会是第二次启动Flutter页面耗时的两倍左右,这样给人感觉很不好.分析发现第一次启动 ...
- flutter 局部状态和全局状态区别_给 Android 开发者的 Flutter 指南
这篇文档旨在帮助 Android 开发者利用既有的 Android 知识来通过 Flutter 开发移动应用.如果你了解 Android 框架的基本知识,你就可以使用这篇文档作为 Flutter 开发 ...
- Flutter 1.17 | 2020 首个稳定版发布!
作者 / Chris Sells, Product Manager, Flutter developer experience 很高兴为大家带来 Flutter 1.17,这也是我们 2020 年的第 ...
- 定位Flutter内存问题很难么?
简介:flutter内存泄漏定位 作者:闲鱼技术-三莅 内存水位升高导致的稳定性问题严重影响app用户体验,所以开发者们非常关注Flutter的内存表现.随着Flutter业务越来越多,闲鱼也面临着o ...
- 超详解析Flutter渲染引擎|业务想创新,不了解底层原理怎么行?
简介:Flutter 作为一个跨平台的应用框架,诞生之后,就被高度关注.它通过自绘 UI ,解决了之前 RN 和 weex 方案难以解决的多端一致性问题.Dart AOT 和精减的渲染管线,相对与 J ...
最新文章
- RMB符号的几种显示方式。
- 使用SharedPreferences进行数据存储
- 《从技术走向管理——李元芳履职记》读书心得
- c语言程序设计多数求最大值,C语言编程问题存在三个整数,使x²+y²+z²=625,求x+y+z的最大值。这是大一C语言实验书的习题,请不要用过于复杂的算法语句...
- 如何安装 Linux 下的 Adobe Reader
- 实际开发的存储过程_实际生产中的 Android SDK开发总结| 完结
- DropBox:机器学习每年可以为我们节省170万的文档预览费用
- 微信小程序项目实例——投骰子
- WPS标题编号级别,根据上一级自动编号
- 怎么进入修复计算机界面,开机进入启动修复界面不能启动win7电脑的修复办法...
- 官方大大的的邮寄快递到啦
- Zoom天使投资人Jim Scheinman:Zoom的诞生始末
- 利用cookie进行模拟登录并且抓取失败
- 线性非齐次微分方程的求解套路
- 【回溯专题】—— 回溯算法入门篇
- while (0) 是什么东东?
- 怎么用计算机打吃鸡,想要用电脑玩绝地求生手游吗?这几个模拟器满足你
- R语言ggplot2可视化:使用patchwork包将两个ggplot2可视化结果图像垂直堆叠排列进行组合构图(vertically stack the plots)
- wordpress添加媒体_如何在WordPress中添加新帖子并利用所有功能
- Ubuntu 安装一款常用的图像编辑软件
热门文章
- 餐厅点菜c语言程序代码,餐馆点菜系统C语言源代码.pdf
- Cannot connenct to relay host smtp.163.com (php邮件发送失败)
- 重力传感事件应用之一 手机摇一摇(摇一次得一分)
- 【数据分析】基于RFM模型的线上零售中的客户细分(二):RFM模型实战
- 企业级的周报管理系统源码 采用了目前极为流行的扁平化响应式的设计风格
- NVIDIA Jetson TK1学习与开发——简介(针对嵌入式系统应用释放 GPU 的潜能)
- 启动Hadoop都哪些进程?它们的作用是什么
- 标梵微信小程序开发价格之做一个小程序要多少钱?
- HTML朗读可以用英文吗,关于英语朗读的方法技巧
- Requirement already satisfied的解决方案