杰理 SDK 文档中心
首页
首页
  • 项目概述

    • 项目简介与核心能力
    • 运行环境与SDK版本
  • 快速开始

    • 工程导入与依赖配置
    • 权限配置与示例运行
  • 平台架构

    • SDK分层架构与RCSP协议
    • 蓝牙连接库
    • 健康SDK核心库 JL_Watch
    • 健康服务器与云端服务
  • 健康与运动数据

    • 健康数据同步
    • 运动数据同步
    • 本地数据持久化
  • 设备管理功能

    • 表盘管理
    • 闹钟与健康提醒
    • 消息与联系人同步
    • 天气同步
    • 设备查找
    • 支付宝集成
  • 传输与媒体处理

    • 文件传输与文件管理
    • 音乐传输与播放控制
    • 图像转换库
    • 音频编解码与解密
  • OTA 升级

    • 固件空中升级流程
    • 4G模块与差分升级
  • AI 能力

    • AI表盘与云服务
    • AI语音助手
  • 示例应用

    • HealthAide 健康助手应用
    • WatchTestTool 测试工具
  • 开发者指南

    • 自定义命令扩展
    • 调试技巧与问题排查
    • 版本历史与兼容性

健康数据同步

健康数据同步是 HealthAide 应用中负责把杰理(Jieli)智能手表/手环通过低功耗蓝牙(BLE)采集的健康数据(心率、血氧、运动、定位、AI 云端消息等)读取到手机端、解析并持久化到本地 Room 数据库的完整链路,涵盖设备侧同步命令、本地数据库访问层与数据对外形态(VO)。

Purpose and Scope

本页讲解 HealthAide(HealthAide_V1.1.0_SDK_V1.14.0)中"健康数据同步"这一能力的端到端实现:

  • 设备侧同步命令(JL RCSP SDK 中 SportsInfoStatusSyncCmd 等 Sync 命令族)如何被 App 使用;
  • 同步得到的原始数据如何通过 HealthDataDbHelper 单例写入 Room 数据库(HealthDatabase 及其 DAO);
  • 同步数据的实体形态(HealthEntity、SportRecord、LocationEntity、AICloudMessageEntity、User)与对外数据形态(VO,如血氧日/月聚合);
  • 控制同步行为的配置开关(HealthConstant)。

不在本页范围:单类健康指标(如血氧详情)的业务页面、运动轨迹算法、AI 云端对话逻辑分别属于运动记录、血氧数据等兄弟页面;BLE 连接管理与 SDK 初始化细节属于蓝牙连接相关页面。本页只聚焦"数据从设备到本地库再到上层消费"的同步机制本身。

Overview

HealthAide 是一个围绕 JL 系列智能手表/手环的 Android 健康伴侣应用。手表端持续采集用户的健康与运动数据;App 端通过 BLE 连接设备后,使用杰理 RCSP 协议中的同步命令族把设备上的数据拉取到手机,随后在本地完成解析与持久化,供健康看板、历史记录等 UI 消费。

整个同步体系分为三层:

  1. 命令层:以 SportsInfoStatusSyncCmd 为代表的 RCSP 同步命令(固件参数 FirmwareStopSportsParam、实时数据响应 ReadRealDataResponse 等),负责与设备交互;
  2. 持久化层:HealthDataDbHelper(进程内单例)→ HealthDatabase(Room 数据库)→ 各 DAO(HealthDao、SportRecordDao、LocationDao、AICloudMessageDao、UserDao)→ 实体;
  3. 数据形态层:以 BaseVo / BaseParseVo / BaseLiveDataVo 为基类的 VO 体系(如 BloodOxygenDayVo、BloodOxygenMonthVo),把库表记录转换为 UI 可直接渲染的聚合视图。

HealthConstant 中的静态开关(如 SYNC_DEV_POWER)决定同步内容是否启用;VirtualDataHelper 则为无设备场景提供虚拟数据,用于演示与联调。

Architecture

flowchart TD
    subgraph sg_Device["设备侧 (JL Watch)"]
        Watch["JL 手表/手环<br/>(BLE + RCSP 协议)"]
    end

    subgraph sg_Command["同步命令层"]
        SyncCmd["SportsInfoStatusSyncCmd 等<br/>Sync 命令族"]
        ParseCmd["FirmwareStopSportsParam<br/>ReadRealDataResponse 解析"]
    end

    subgraph sg_Persist["持久化层"]
        Helper["HealthDataDbHelper<br/>(DCL 单例)"]
        DB["HealthDatabase<br/>(Room)"]
        Dao["HealthDao / SportRecordDao<br/>LocationDao / AICloudMessageDao"]
    end

    subgraph sg_Entity["实体层"]
        Ent["HealthEntity / SportRecord<br/>LocationEntity / AICloudMessageEntity"]
    end

    subgraph sg_Vo["数据形态层"]
        Vo["BaseVo / BaseParseVo / BaseLiveDataVo<br/>BloodOxygenDayVo / BloodOxygenMonthVo"]
    end

    subgraph sg_UI["上层消费"]
        UI["ViewModel / UI 看板 / 历史记录"]
    end

    Watch -->|"BLE 数据同步"| SyncCmd
    SyncCmd --> ParseCmd
    ParseCmd --> Helper
    Helper --> DB
    DB --> Dao
    Dao --> Ent
    Ent --> Vo
    Vo --> UI

架构解读:数据流是单向的"设备 → 命令 → 解析 → 落库 → 出库"。HealthDataDbHelper 是整个持久化层的唯一入口,采用双重检查锁(DCL)单例,确保全进程只构建一次 HealthDatabase,避免重复建库开销;DAO 层屏蔽了 Room 的 SQL 细节,实体层定义表结构,VO 层负责把存储模型转换成按日/月聚合的展示模型。HealthConstant 的开关(如 SYNC_DEV_POWER)在命令层决定同步哪些数据项。

主内容:同步链路实现剖析

1. 设备侧同步命令(JL RCSP SDK)

健康数据的来源是手表固件。App 通过杰理 RCSP 协议中的同步命令族与设备交互,其中典型代表是 com.jieli.jl_rcsp.model.command.watch.SportsInfoStatusSyncCmd。该命令涵盖运动状态上报、固件停止运动参数、实时数据读取等多种子结构。测试代码展示了其用法:

  • FirmwareStopSportsParam:从固件下发的停止运动参数中解析出 fileId 等字段;
  • ReadRealDataResponse:解析设备实时数据响应。

这类命令统一继承 RCSP 的命令模型,App 侧通过 WatchManager(WatchManagerByJL / WatchManagerByCustom,见 WatchManagerTest)发送与接收,回调中携带解析后的参数对象。同步内容的开关集中在 HealthConstant:

  • SYNC_DEV_POWER(默认 true)——是否同步设备电量;
  • KEY_ASSERT_RES_SYNC(SharedPreferences key)——是否更新 assert 资源文件(固件资源同步)。

2. 持久化层:HealthDataDbHelper 单例

同步解析出的数据统一通过 HealthDataDbHelper 写入本地库。该类是典型的懒加载双重检查锁(DCL)单例:

public class HealthDataDbHelper {
    private final static String TAG = HealthDataDbHelper.class.getSimpleName();
    @SuppressLint("StaticFieldLeak")
    private volatile static HealthDataDbHelper instance;

    private final HealthDatabase mHealthDatabase;

    private HealthDataDbHelper() {
        Context context = HealthApplication.getAppViewModel().getApplication();
        if (null == context) {
            throw new NullPointerException("Application is null.");
        }
        mHealthDatabase = HealthDatabase.buildHealthDb(context);
    }

    public static HealthDataDbHelper getInstance() {
        if (null == instance) {
            synchronized (HealthDataDbHelper.class) {
                if (null == instance) {
                    instance = new HealthDataDbHelper();
                }
            }
        }
        return instance;
    }
    ...
}

Source: HealthDataDbHelper.java

设计意图:

  • volatile + 双重检查保证多线程下仅创建一次实例,同时避免每次获取都加锁的同步开销——同步写入健康数据可能发生在 BLE 回调线程、业务线程等多个线程,必须保证并发安全;
  • 构造时校验 Application 非空并抛出 NullPointerException,把"依赖注入缺失"这类配置错误尽早暴露,而不是等到首次写库时才失败;
  • @SuppressLint("StaticFieldLeak") 是已知取舍:单例持有 HealthDatabase(间接持有 Context),换取全进程唯一数据库连接,避免多个连接实例间的写冲突与内存膨胀。

实例对外暴露 5 个访问器,供同步落库与查询两侧使用:

public HealthDao getHealthDao() {
    return mHealthDatabase.HealthDao();
}

public SportRecordDao getSportRecordDao() {
    return mHealthDatabase.SportRecordDao();
}

public LocationDao getLocationDao() {
    return mHealthDatabase.LocationDao();
}

public AICloudMessageDao getAICloudMessageDao() {
    return mHealthDatabase.AICloudMessageDao();
}

public HealthDatabase getHealthDatabase() {
    return mHealthDatabase;
}

Source: HealthDataDbHelper.java

3. 数据库与 DAO 划分

HealthDatabase(Room 数据库,通过静态工厂 buildHealthDb(context) 构建)按业务域拆分为 5 个 DAO,对应 5 类同步数据:

DAO对应实体同步数据类型
HealthDaoHealthEntity健康指标(心率、血氧等)
SportRecordDaoSportRecord运动记录(含固件 fileId 关联)
LocationDaoLocationEntity运动轨迹 GPS 定位点
AICloudMessageDaoAICloudMessageEntityAI 云端消息(对话/语义结果)
UserDaoUser用户信息

这种拆分遵循单一职责:不同来源(设备传感器、运动引擎、云端 AI)的数据写入互不干扰,同步任务可以按数据域独立调度;同时每个 DAO 独立承担增删改查,UI 层可按需只观察某一类数据的变化。

注:HealthDao、HealthDatabase 内部 SQL/迁移实现细节不在本次源码读取范围内,本页仅描述其在同步链路中的职责与接口契约。

4. 数据形态层:VO 聚合

data/vo 包提供三层基类与具体 VO:

  • BaseVo:VO 根基类;
  • BaseParseVo:负责把设备原始字节/命令参数解析为 VO(解析职责下沉到 VO 层,保持 DAO 只做存储);
  • BaseLiveDataVo:与 LiveData 结合的响应式 VO,同步完成即可通知 UI。

血氧模块是典型示例:BloodOxygenBaseVo → BloodOxygenDayVo(按日聚合)/ BloodOxygenMonthVo(按月聚合)。同步命令每收到一段血氧数据,解析后先写入 HealthEntity,再经 DAO 查询聚合出日/月视图,供看板图表直接消费。

5. 虚拟数据:VirtualDataHelper

data/db/VirtualDataHelper 用于无真实设备场景:按 HealthConstant 的开关生成模拟健康数据写入同一套 DAO,使 UI、图表与同步链路在无手表时也能完整跑通(联调/演示/自动化测试)。它复用同一持久化入口,保证"真实同步"与"虚拟同步"对上层完全透明。

Core Flow

sequenceDiagram
    participant W as JL 手表 (BLE)
    participant B as BleManager / WatchManager
    participant P as 命令解析 (SportsInfoStatusSyncCmd)
    participant H as HealthDataDbHelper (单例)
    participant D as Room: HealthDatabase + DAO
    participant V as VO / ViewModel
    participant U as UI 看板

    W->>B: 上报/响应健康数据 (RCSP)
    B->>P: 分发 Sync 命令
    P->>P: 解析 FirmwareStopSportsParam / ReadRealDataResponse
    P->>H: 写入解析结果 (健康/运动/定位/消息)
    H->>D: insert/update 对应 DAO
    D-->>H: 行 ID / 结果
    H-->>P: 写库完成回调
    P->>V: BaseLiveDataVo 通知数据变更
    V->>U: 刷新看板/历史记录
    D-->>V: 查询聚合 (BloodOxygenDayVo/MonthVo)

流程要点:同步是"拉取-解析-落库-通知"四步。命令解析层(P)与持久化层(H)通过 DAO 接口解耦,使新增一种健康数据类型只需新增命令解析与对应 DAO 写入,无需改动单例与数据库骨架;写库完成后通过 LiveData 形态的 VO 主动推送,避免 UI 轮询。

Usage Examples

基础用法:获取同步落库入口

任何需要写健康数据的同步模块,都通过单例拿到对应 DAO:

HealthDataDbHelper helper = HealthDataDbHelper.getInstance();
HealthDao healthDao = helper.getHealthDao();          // 健康指标
SportRecordDao sportRecordDao = helper.getSportRecordDao(); // 运动记录
LocationDao locationDao = helper.getLocationDao();    // 定位轨迹
AICloudMessageDao aiDao = helper.getAICloudMessageDao();    // AI 云端消息

Source: HealthDataDbHelper.java

进阶用法:解析设备同步命令

测试代码展示了固件同步命令的解析模式——先构造参数/响应对象,再从对象中取字段:

SportsInfoStatusSyncCmd.FirmwareStopSportsParam param =
        new SportsInfoStatusSyncCmd.FirmwareStopSportsParam(data);
System.out.println("param : " + param.getFileId());

Source: StringTest.java

实时数据响应同样以命令响应对象承载,解析失败时抛 ParseDataException,调用方需捕获处理:

SportsInfoStatusSyncCmd.ReadRealDataResponse response = null;
try {
    response = new SportsInfoStatusSyncCmd.ReadRealDataResponse(data);
} catch (ParseDataException e) {
    // 处理解析失败(数据长度不符、校验失败等)
}

Source: StringTest.java

同步开关配置

//同步设备电量
public final static boolean SYNC_DEV_POWER = true;
...
public static final String KEY_ASSERT_RES_SYNC = "key_assert_res_sync";//更新assert资源文件

Source: HealthConstant.java 、HealthConstant.java

Configuration Options

配置项类型默认值说明
HealthConstant.SYNC_DEV_POWERbooleantrue是否同步设备电量(编译期静态开关,改动需重新编译)
HealthConstant.KEY_ASSERT_RES_SYNCString"key_assert_res_sync"SharedPreferences 键,标记是否更新 assert 资源文件(固件资源同步)
HealthApplication 的 Application 注入Context由 AppViewModel 提供HealthDataDbHelper 构造时必需;为 null 时抛 NullPointerException

Failure Modes, Edge Cases & Concurrency

并发安全

  • HealthDataDbHelper.getInstance() 使用 volatile + synchronized 双重检查锁,保证 BLE 回调线程、业务线程同时首次调用时只构建一次数据库实例;mHealthDatabase 为 final,构建后不可替换,避免运行期状态漂移。
  • Room 数据库内部对写操作串行化,多个同步任务(健康、运动、定位、AI 消息)并发写入各自 DAO 不会互相阻塞(分表分 DAO 的设计本身即是为并发同步服务)。

初始化失败

  • 构造 HealthDataDbHelper 时若 HealthApplication.getAppViewModel().getApplication() 返回 null(例如在 Application 初始化完成前调用),会立即抛出 NullPointerException——这是有意为之的快速失败(fail-fast),防止后续所有同步写入静默失败。

解析失败

  • 设备返回的原始字节若长度或结构不符,FirmwareStopSportsParam / ReadRealDataResponse 等构造器会抛 ParseDataException,同步流程必须在 catch 中降级处理(丢弃该包、记录日志并继续后续同步),不能中断整条同步链路。

静态引用泄漏(已知取舍)

  • @SuppressLint("StaticFieldLeak") 表明单例静态持有数据库实例是已知且接受的:以潜在 Context 泄漏风险换取全局唯一数据库连接与更低的内存开销。进程生命周期与应用一致,实际影响可控。

Performance & Operational Considerations

  • 单例复用:HealthDatabase 全进程唯一,避免重复打开数据库;Room 自带连接池与查询缓存,同步写入密集场景下吞吐可控。
  • 分 DAO 写入:健康/运动/定位/AI 消息分表存储,同步任务可并行、可按数据域独立调度,避免单一表锁竞争。
  • VO 聚合下沉:日/月聚合(BloodOxygenDayVo / BloodOxygenMonthVo)在查询层完成,UI 只消费结果,减少主线程计算。
  • 操作建议:批量同步(如历史数据回传)应放在后台线程/协程执行,避免占用主线程;SYNC_DEV_POWER 等编译期开关变更需重新构建 App。

Extension Points

  • 新增同步数据类型:参照现有模式——在 RCSP SDK 命令族中增加命令解析 → 新增/复用 HealthEntity 字段 → 在 HealthDao 增加对应读写 → 在 data/vo 增加聚合 VO 并接入 BaseLiveDataVo。HealthDataDbHelper 与 HealthDatabase 骨架无需改动。
  • 虚拟数据扩展:VirtualDataHelper 可扩展生成任意新数据域的模拟数据,使 UI 与测试在无设备时先行开发。
  • 同步开关扩展:在 HealthConstant 增加布尔开关即可按编译期控制新数据项的同步启停,与 SYNC_DEV_POWER 保持一致模式。

Tests

  • StringTest.java:验证 SportsInfoStatusSyncCmd 固件参数与实时数据响应的字节解析(FirmwareStopSportsParam.getFileId()、ReadRealDataResponse 构造),覆盖解析成功与 ParseDataException 异常路径,是同步命令层正确性的回归保障。
  • WatchManagerTest.java:验证 WatchManagerByCustom、WatchManagerByJL、BleManager 的单例初始化(均使用 synchronized 双重检查),保证同步命令收发所依赖的连接管理组件线程安全。

Related Links

  • HealthDataDbHelper.java
  • HealthDatabase.java
  • HealthDao.java
  • HealthConstant.java
  • StringTest.java
  • WatchManagerTest.java
  • 相关兄弟页面:运动记录(SportRecord)、血氧数据(blood_oxygen 聚合)、蓝牙连接管理
Next
运动数据同步