杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • 项目简介与功能总览
    • 运行环境与快速开始
    • 工程结构与文档布局
  • 架构与核心机制

    • 插件架构与原生平台桥接
    • 基类管理器与常量体系
    • 事件流与接收通知机制
  • 蓝牙连接与设备管理

    • 蓝牙连接与状态管理
    • 设备信息、配置与按键设置
    • 双设备连接与多链路管理
    • 数据传输与自定义命令
  • 音乐与媒体控制

    • 设备音乐与手机音乐播放控制
    • 音量与音频输出管理
  • 音效与音频模式

    • 均衡器与音效调节
    • 音频模式与降噪(ANC)设置
    • Auracast 音频广播
  • 设备功能控制

    • 闹钟管理
    • FM 收音机控制
    • 灯光控制
    • 充电仓与彩屏仓管理
  • 示例应用:杰理之家 Demo

    • 应用框架与交互组件
    • 设置、多语言与调试
  • 接口参考与文档中心

    • 发送接口参考
    • 接收接口与事件参考
    • 官方文档与集成指南

设备信息、配置与按键设置

本页介绍 JL_Home SDK 中设备信息获取、配置数据转换与按键(keySettings)/LED(ledSettings)设置的完整数据链路:从 Flutter 管理器的 MethodChannel 调用,到数据模型转换,再到示例 App 的设置页面渲染与刷新。

Purpose and Scope

本页覆盖以下能力:

  • 设备信息查询:BleDeviceInfoManager 提供设备类型、设备信息、底部卡片、功能列表等查询接口;
  • 配置数据转换:示例 App 的 DeviceInfoManager 将原生返回的 Map<Object?, Object?> 转换为可渲染的配置结构,重点处理 keySettings(按键设置)与 ledSettings(LED 设置)列表;
  • 按键设置与配置页面:device_settings_page.dart 如何加载、展示并在配置变更后刷新设备信息;
  • 方法常量桥接:BleMethodConstants 中与本能力相关的方法名常量定义。

以下内容不在本页范围,属于姊妹页面:BLE 连接与断连流程(见"连接管理"页)、OTA 升级、音乐/闹钟管理、灯光(light)设置等独立能力。

Overview

JL_Home 是一个基于 Flutter 的杰理(Jieli)蓝牙音频设备 App 示例。SDK 采用分层桥接架构:Dart 层管理器(lib/manager/)通过 BleBaseManager.invokeMethod 调用原生平台(Android/iOS)的 MethodChannel 方法,原生 SDK 再与 BLE 设备通信,把设备能力以 JSON/Map 形式回传。

设备信息是本能力的数据基础。一次 getDeviceInfo 调用返回的 Map 中通常包含:

  • 设备类型、名称、协议版本等基础属性;
  • keySettings:按键事件 → 动作映射表(例如单击/双击/长按对应的功能);
  • ledSettings:LED 灯效配置表;
  • 设备支持的功能列表与底部卡片类型等。

设计意图:信息获取与 UI 渲染解耦。SDK 侧只提供原始数据接口,示例 App 侧用 DeviceInfoManager 做"翻译层",这样不同设备固件的字段差异被隔离在转换层内,页面代码只需消费统一的 Map<String, dynamic>。

Architecture

flowchart TD
    subgraph sg_App["示例 App(JieLi_Home_Demo)"]
        Page["DeviceSettingsPage<br/>device_settings_page.dart"]
        DM["DeviceInfoManager<br/>数据转换层"]
        BDM["BleDeviceInfoManager<br/>设备信息管理器"]
        BSM["BleDeviceSettingManager<br/>设置管理器"]
    end

    subgraph sg_Sdk["jl_home SDK(Flutter 层)"]
        Base["BleBaseManager<br/>invokeMethod 桥接"]
        Const["BleMethodConstants<br/>方法名常量"]
    end

    subgraph sg_Native["原生平台"]
        Native["MethodChannel 原生实现<br/>(Android/iOS SDK)"]
        BleDev["BLE 设备(固件)"]
    end

    Page -->|"getDeviceInfo / convert"| DM
    DM --> BDM
    Page --> BDM
    Page --> BSM
    BDM -->|"invokeMethod(methodGetDeviceInfo)"| Base
    BSM --> Base
    Base --> Const
    Base -->|"方法调用与结果回传"| Native
    Native <-->|"BLE 协议通信"| BleDev

架构说明:页面层只依赖管理器公开的静态方法;管理器把所有调用收敛到 BleBaseManager.invokeMethod,方法名统一来自 BleMethodConstants;原生层负责实际 BLE 通信并回传 Map/List 结果。DeviceInfoManager 是示例 App 侧的纯 Dart 转换层,不参与平台桥接。

主要模块与实现分析

BleDeviceInfoManager — 设备信息管理器

BleDeviceInfoManager(ble_device_info_manager.dart)是 SDK 对外暴露的设备信息查询入口,全部为静态方法,内部统一通过 BleBaseManager.invokeMethod 发起平台调用。

方法平台方法常量返回类型说明
getCurrentDeviceType()methodCurrentDeviceTypeFuture<int>当前设备类型
getDeviceInfo()methodGetDeviceInfoFuture<Map<String, dynamic>>完整设备信息(含按键/LED 配置)
getCardBottomArray()methodGetCardBottomArrayFuture<List<int>>底部卡片类型数组
getSupportedFunctions()methodGetCurrentDeviceFunctionsFuture<List<String>>当前设备支持的功能列表
getFunctionCommon()methodFunctionCommonFuture<void>获取通用功能模式

关键实现细节:

  1. 严格失败策略(getDeviceInfo):当平台返回 null 时直接抛出 Exception('Failed to get device info'),而不是返回空 Map。设计意图是让上层尽早感知"信息不可用"这一异常状态,避免用空数据渲染出错误的配置界面。
  2. 宽松失败策略(getCardBottomArray / getSupportedFunctions):这两个接口在异常或空结果时分别回退到 [0] 与 []。设计意图相反——卡片与功能列表属于"可降级展示"的数据,宁可显示默认值也不阻塞页面。
  3. 结果类型归一:getDeviceInfo 把平台返回的 result 强制转为 Map<String, dynamic>,保证上层拿到的是统一的字符串键 Map。

DeviceInfoManager — 数据转换层(示例 App)

DeviceInfoManager(device_info_manager.dart)位于示例 App 内,解决一个关键痛点:MethodChannel 回传的 Map 键类型是 Object?,且嵌套的 keySettings/ledSettings 元素本身也是 Map<Object?, Object?>,直接渲染会因类型不匹配而失败。

Map<String, dynamic> convertDeviceInfo(Map<Object?, Object?> rawData) {
  final converted = <String, dynamic>{};

  rawData.forEach((key, value) {
    if (key is String) converted[key] = value;
  });

  if (converted['keySettings'] is List) {
    converted['keySettings'] = (converted['keySettings'] as List)
        .map((item) => AppUtil.convertMap(item as Map<Object?, Object?>))
        .toList();
  }

  if (converted['ledSettings'] is List) {
    converted['ledSettings'] = (converted['ledSettings'] as List)
        .map((item) => AppUtil.convertMap(item as Map<Object?, Object?>))
        .toList();
  }

  return converted;
}

Source: device_info_manager.dart

转换逻辑分三步:

  1. 键类型过滤:只保留 String 类型的键,丢弃非字符串键,防止后续 _deviceInfo['keySettings'] 这类索引操作出现类型错误;
  2. keySettings 深转换:将按键配置列表中的每个元素(Map<Object?, Object?>)用 AppUtil.convertMap 递归转为 Map<String, dynamic>;
  3. ledSettings 深转换:与按键同理,处理 LED 灯效配置列表。

此外该类持有 deviceType 字段,loadDeviceType() 在页面初始化时调用 BleDeviceInfoManager.getCurrentDeviceType() 缓存设备类型,供功能开关的显隐判断使用。

DeviceSettingsPage — 配置页面加载与刷新

DeviceSettingsPage(device_settings_page.dart)是按键/配置设置的 UI 入口。其 _getDeviceInfo 私有方法展示了完整的数据流:

Future<void> _getDeviceInfo({bool silent = false}) async {
  final deviceInfo = await BleDeviceInfoManager.getDeviceInfo();
  _deviceInfo = _deviceInfoManager.convertDeviceInfo(deviceInfo);
  // ... setState 更新 UI
}

Source: device_settings_page.dart

silent 参数控制是否静默刷新(不显示 loading)。页面在三种时机触发刷新:

  • 首次进入(initState 流程,silent: false):先 loadDeviceType() 再 _getDeviceInfo();
  • 配置操作完成后(如按键设置保存):.then((_) => _getDeviceInfo(silent: false)) 重新拉取最新配置;
  • 设置变更后静默同步:部分操作使用 silent: true,不打断用户操作。

按键设置列表的渲染入口位于 _deviceInfo['keySettings']:

if (_deviceInfo['keySettings'] != null) {
  List<Map<String, dynamic>> keySettings =
      List<Map<String, dynamic>>.from(_deviceInfo['keySettings']!);
  // ... 构建按键项 UI
}

Source: device_settings_page.dart

页面还通过 BleDeviceSettingManager(ble_device_setting_manager.dart)下发配置修改指令,并在回调后刷新信息;UI 构件由 device_settings_ui_builder.dart 按设备类型动态生成。

BleMethodConstants — 方法名常量

平台桥接方法名统一收敛在 ble_method_constants.dart,例如:

/// 获取设备信息
static const String methodGetDeviceInfo = 'getDeviceInfo';

Source: ble_method_constants.dart

集中管理常量避免了字符串散落在各处导致拼写错误,并让原生端与 Dart 端共享同一份方法名契约。

核心流程

设备信息加载时序

sequenceDiagram
    participant Page as DeviceSettingsPage
    participant DM as DeviceInfoManager
    participant BDM as BleDeviceInfoManager
    participant Base as BleBaseManager
    participant Native as 原生 SDK / BLE

    Page->>DM: loadDeviceType()
    DM->>BDM: getCurrentDeviceType()
    BDM->>Base: invokeMethod(methodCurrentDeviceType)
    Base->>Native: MethodChannel 调用
    Native-->>Base: int deviceType
    Base-->>BDM: int
    BDM-->>DM: int
    DM-->>Page: 缓存 deviceType

    Page->>BDM: getDeviceInfo()
    BDM->>Base: invokeMethod(methodGetDeviceInfo)
    Base->>Native: MethodChannel 调用
    Native->>Native: 与 BLE 固件交互,组装 keySettings/ledSettings
    Native-->>Base: Map<Object?, Object?>
    Base-->>BDM: Map
    BDM-->>Page: Map<String, dynamic>

    Page->>DM: convertDeviceInfo(raw)
    DM->>DM: 过滤 String 键 + 深转换 keySettings/ledSettings
    DM-->>Page: 统一 Map<String, dynamic>
    Page->>Page: setState 渲染配置界面

    Note over Page,Native: 配置修改后 .then((_) => _getDeviceInfo(silent: false)) 重新拉取

时序要点:整个链路是单向请求-响应模式,无长连接推送。任何一次配置变更后都必须显式重新调用 getDeviceInfo 才能拿到最新状态,这是示例 App 中"修改→刷新"回调模式(_getDeviceInfo(silent: false))存在的根本原因。

失败回退流程

  • getDeviceInfo 返回 null → 抛 Exception → 页面捕获后按错误处理(不渲染空配置);
  • getCardBottomArray 异常 → 回退 [0],页面按默认卡片渲染;
  • getSupportedFunctions 异常 → 回退 [],功能列表为空时页面隐藏相关入口;
  • 设备断开(_handleDisconnection)→ 触发 _getDeviceInfo(silent: true) 兜底刷新,避免界面停留在已断开设备的状态。

使用示例

示例一:查询并转换设备信息(核心链路)

static Future<Map<String, dynamic>> getDeviceInfo() async {
  final result = await BleBaseManager.invokeMethod(
    BleMethodConstants.methodGetDeviceInfo,
  );

  if (result != null) {
    return Map<String, dynamic>.from(result);
  } else {
    throw Exception('Failed to get device info');
  }
}

Source: ble_device_info_manager.dart

示例二:加载设备类型缓存

Future<void> loadDeviceType() async {
  deviceType = await BleDeviceInfoManager.getCurrentDeviceType();
}

Source: device_info_manager.dart

示例三:底部卡片数组的容错读取

static Future<List<int>> getCardBottomArray() async {
  try {
    final result = await BleBaseManager.invokeMethod(
      BleMethodConstants.methodGetCardBottomArray,
    );
    if (result is List) {
      return result.cast<int>();
    }
    return [0];
  } catch (e) {
    return [0];
  }
}

Source: ble_device_info_manager.dart

示例四:支持的功能列表查询(异常时返回空列表)

static Future<List<String>> getSupportedFunctions() async {
  try {
    final result = await BleBaseManager.invokeMethod(
      BleMethodConstants.methodGetCurrentDeviceFunctions,
    );
    return List<String>.from(result as List);
  } on Exception {
    return [];
  }
}

Source: ble_device_info_manager.dart

API Reference

BleDeviceInfoManager(静态方法)

static Future<int> getCurrentDeviceType()

获取当前连接的设备类型,返回值为设备类型 ID。由页面/管理器在初始化时调用并缓存。

  • Returns: int 设备类型
  • Throws: 平台调用异常(由 BleBaseManager.invokeMethod 透传)

static Future<Map<String, dynamic>> getDeviceInfo()

获取完整设备信息,包含 keySettings(按键设置)、ledSettings(LED 设置)等配置数据。

  • Returns: Map<String, dynamic> 设备信息
  • Throws: Exception('Failed to get device info') — 当平台返回 null 时抛出

static Future<List<int>> getCardBottomArray()

获取底部卡片类型数组。

  • Returns: List<int>;平台结果不是 List 或调用异常时回退为 [0]

static Future<List<String>> getSupportedFunctions()

获取当前设备支持的功能名列表。

  • Returns: List<String>;异常时返回 []

static Future<void> getFunctionCommon()

请求设备返回通用功能模式(单向触发,不解析返回值)。

  • Returns: Future<void>

DeviceInfoManager(示例 App)

Future<void> loadDeviceType()

调用 BleDeviceInfoManager.getCurrentDeviceType() 并缓存到 deviceType 字段。

Map<String, dynamic> convertDeviceInfo(Map<Object?, Object?> rawData)

把 MethodChannel 原始返回值转换为 UI 可直接消费的 Map<String, dynamic>:

  • 过滤非 String 键;
  • 对 keySettings 与 ledSettings 列表逐元素执行 AppUtil.convertMap 深转换。

方法常量与配置

平台桥接方法名常量(定义于 BleMethodConstants,ble_method_constants.dart):

常量名字符串值用途
methodCurrentDeviceTypecurrentDeviceType获取设备类型
methodGetDeviceInfogetDeviceInfo获取设备信息(含按键/LED 配置)
methodGetCardBottomArraygetCardBottomArray底部卡片数组
methodGetCurrentDeviceFunctionsgetCurrentDeviceFunctions设备支持的功能列表
methodFunctionCommonfunctionCommon通用功能模式

设备信息 Map 中与配置相关的键:

键名类型说明
keySettingsList<Map<String, dynamic>>按键事件 → 动作映射表,页面据此渲染按键设置项
ledSettingsList<Map<String, dynamic>>LED 灯效配置表

失败模式、边界情况与并发

  • 平台返回 null:getDeviceInfo 抛 Exception,调用方(页面 _getDeviceInfo)依赖 try/catch 处理,silent: false 时展示加载错误提示。
  • 类型不匹配:MethodChannel 回传的嵌套 Map 键为 Object?,若跳过 convertDeviceInfo 直接访问 _deviceInfo['keySettings'] 会触发类型错误——转换层是必经之路。
  • 降级默认值:getCardBottomArray 失败回退 [0]、getSupportedFunctions 失败回退 [],保证页面可降级渲染而非崩溃。
  • 设备断开竞态:连接状态变化与 getDeviceInfo 异步返回可能交错;页面在断连回调中触发 _getDeviceInfo(silent: true) 兜底刷新,避免残留旧设备数据。
  • 重复刷新:getDeviceInfo 是无状态查询,多次调用安全;但页面以"修改→刷新"模式工作,高频配置操作会带来多次平台调用,silent 参数用于抑制 UI 抖动。
  • 并发注意:BleDeviceInfoManager 全部为静态方法且无内部共享可变状态(DeviceInfoManager.deviceType 是唯一的页面级缓存),因此不存在多实例状态污染问题;但同一时刻多个页面并发调用平台通道时,结果按 await 顺序各自归位,页面需自行校验结果归属(实践中由单连接管理器串行化)。

扩展点

  • 新增设备信息字段:原生端在 getDeviceInfo 返回值中增加键,Dart 侧无需改动管理器;如需 UI 展示,在 convertDeviceInfo 中补充深转换逻辑并在 device_settings_ui_builder.dart 中按 deviceType 添加对应构件。
  • 新增配置下发:参照 BleDeviceSettingManager 的模式,新增静态方法 + 平台方法常量,页面修改后复用 _getDeviceInfo(silent: false) 刷新。
  • 多设备类型适配:deviceType 字段与 DeviceSettingsUiBuilder 的组合是当前扩展多固件 UI 的既定路径。

Related Links

  • BleDeviceInfoManager 源码
  • DeviceInfoManager 源码
  • DeviceSettingsPage 源码
  • BleMethodConstants 源码
  • BleDeviceSettingManager 源码
  • 相关能力页:连接管理、OTA 升级、音乐播放、灯光设置、闹钟管理
Prev
蓝牙连接与状态管理
Next
双设备连接与多链路管理