基于 HarmonyOS Next 的运动健康应用实战:心率监测与健康报告系统
基于 HarmonyOS Next 的运动健康应用实战:心率监测与健康报告系统
本文将深入探讨如何使用 HarmonyOS SDK 和 AppGallery Connect 构建一个功能丰富的运动健康应用。我们将聚焦核心功能:心率实时监测、数据云端存储、健康报告生成及成就激励系统。
一、核心功能设计与技术栈
核心模块:
- 心率监测: 利用设备传感器获取实时心率数据
- 数据同步: 将心率数据安全存储至 AppGallery Connect 云数据库
- 健康报告: 使用云函数分析数据生成每日/每周报告
- 成就系统: 基于运动数据展示排行榜和勋章
技术栈:
- 前端: HarmonyOS ArkTS UI 框架
- 数据存储: AppGallery Connect 云数据库 (NoSQL)
- 后端逻辑: AppGallery Connect 云函数
- 数据采集: HarmonyOS 传感器框架
二、实时心率监测实现 (ArkTS)
1. 权限申请与传感器初始化
// pages/HeartRateMonitor.ets import sensor from **********'; import abilityAccessCtrl, { Permissions } from **********'; // 1. 动态申请心率传感器权限 async function requestPermission(): Promise<void> { try { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); let permission: Permissions = 'ohos.permission.HEALTH_DATA'; // 健康数据权限 await atManager.requestPermissionsFromUser(getContext(this), [permission]); console.info('心率权限申请成功'); } catch (err) { console.error(`权限申请失败: ${err.code}, ${err.message}`); } } // 2. 初始化心率传感器 let heartRateSensor: sensor.HeartRateResponse | null = null; function initHeartRateSensor(): void { try { // 获取心率传感器实例 heartRateSensor = sensor.getSensor(sensor.SensorId.HEART_RATE); if (!heartRateSensor) { console.error('设备不支持心率传感器'); return; } // 设置传感器数据回调函数 heartRateSensor.on('data', (data: sensor.HeartRateResponse) => { if (data.heartRate) { // 更新UI显示实时心率 (假设在组件中定义了@State heartRate: number = 0) this.heartRate = data.heartRate; console.info(`实时心率: ${this.heartRate} bpm`); // 将有效心率数据同步到云端 (稍后实现) this.uploadHeartRateData(this.heartRate); } }); // 设置错误回调 heartRateSensor.on('error', (err: BusinessError) => { console.error(`心率传感器错误: ${err.code}, ${err.message}`); }); } catch (err) { console.error(`初始化传感器失败: ${err.code}, ${err.message}`); } } // 3. 启动和停止监听 function startMonitoring(): void { if (heartRateSensor) { try { // 设置采样率 (NORMAL: 约5秒一次,适合连续监测) heartRateSensor.setInterval(sensor.Interval.NORMAL); heartRateSensor.start(); } catch (err) { console.error(`启动监测失败: ${err.code}, ${err.message}`); } } } function stopMonitoring(): void { if (heartRateSensor) { try { heartRateSensor.stop(); } catch (err) { console.error(`停止监测失败: ${err.code}, ${err.message}`); } } } // 在页面显示时初始化并请求权限 aboutToAppear(): void { requestPermission().then(() => { initHeartRateSensor(); }); } // 页面隐藏时停止传感器以节省资源 aboutToDisappear(): void { stopMonitoring(); }
代码说明:
- 权限申请 (
requestPermission
): 使用abilityAccessCtrl
动态申请HEALTH_DATA
权限,确保用户知情同意。 - 传感器获取 (
initHeartRateSensor
): 通过sensor.getSensor()
获取心率传感器实例。 - 数据监听 (
on('data')
): 注册回调函数,当有新心率数据时更新UI并触发上传。 - 生命周期管理 (
aboutToAppear
/aboutToDisappear
): 在页面显示时初始化并开始监听,页面隐藏时停止监听,优化性能和电量消耗。
三、数据同步至 AppGallery Connect 云数据库
1. 配置云数据库
- 在 AppGallery Connect 控制台创建项目,启用 云数据库 (Cloud DB)。
- 创建对象类型
HeartRateData
,包含字段: userId: String (用户ID)heartRate: Integer (心率值)timestamp: Date (时间戳) - 配置数据存储位置和访问规则(建议设置用户级权限)。
2. ArkTS 数据上传代码
// utils/CloudDBManager.ets import cloud from **********'; import { HeartRateData } from '../model/HeartRateData'; // 假设定义的模型类 // 初始化 Cloud DB 服务 const agcCloud = cloud.agconnectCloudDb(); const cloudDbZone = agcCloud.agconnectCloudDbZone(); // 获取默认或指定Zone // 定义数据模型类 (需与云端对象类型一致) export class HeartRateData { userId: string = ''; heartRate: number = 0; timestamp: Date = new Date(); // 构造函数用于创建实例 constructor(userId: string, heartRate: number, timestamp: Date) { this.userId = userId; this.heartRate = heartRate; this.timestamp = timestamp; } // 可选:定义对象类型名 (需与云端ObjectTypeName一致) static getObjectTypeName(): string { return 'HeartRateData'; } } // 上传心率数据到 Cloud DB export async function uploadHeartRateData(heartRate: number): Promise<void> { try { // 1. 获取当前登录用户ID (需集成AGC Auth服务) const currentUser = getCurrentUser(); // 假设已实现获取当前用户 if (!currentUser) { console.warn('用户未登录,无法上传数据'); return; } // 2. 创建数据对象 const hrData = new HeartRateData( currentUser.uid, heartRate, new Date() // 使用当前时间戳 ); // 3. 执行 Upsert 操作 (存在则更新,不存在则插入) const upsertResult = await cloudDbZone.upsert(HeartRateData.getObjectTypeName(), [hrData]); console.info('心率数据上传成功!', upsertResult); } catch (err) { console.error(`上传心率数据失败: ${err.code}, ${err.message}`); // 此处可添加重试逻辑或本地缓存策略 } } // 在 HeartRateMonitor.ets 的 data 回调中调用 uploadHeartRateData(this.heartRate);
代码说明:
- 模型定义 (
HeartRateData
): 定义与云端对象类型结构一致的本地模型类。 - 初始化 (
agcCloud
,cloudDbZone
): 获取 Cloud DB 服务和操作区域实例。 - 数据操作 (
upsert
):upsert
是插入或更新数据的常用方法。将封装好的HeartRateData
对象插入云数据库。 - 用户关联 (
userId
): 数据记录关联当前用户ID,确保用户只能访问自己的数据(需结合 AppGallery Connect 认证服务)。 - 错误处理: 捕获并记录上传错误,生产环境应添加重试或本地暂存逻辑。
四、生成健康报告 (AppGallery Connect 云函数)
1. 云函数逻辑 (Node.js)
- 场景: 每天凌晨计算用户前一天的静息心率、平均心率、最高心率、最低心率。
// functions/generateDailyReport.js (云函数代码) const agconnect = require('@agconnect/common-server'); const cloud = require('@agconnect/cloud-function-server'); const cloudDb = require('@agconnect/database-server').cloudDb; // 1. 定义云函数入口 exports.main = async function (event) { // 初始化AGC服务 (使用默认配置) agconnect.instance().init(); // 2. 获取 Cloud DB 服务实例和Zone const agcCloudDb = cloudDb.cloudDb(); const cloudDbZone = agcCloudDb.agconnectCloudDbZone(); // 3. 获取所有需要生成报告的用户ID (简化示例,实际可能需分页或定时触发) // 通常根据业务需求获取特定用户列表 // 4. 计算指定用户前一天的心率统计数据 const calculateStats = async (userId) => { // 构建查询:前一天00:00:00 到 23:59:59 的心率数据 const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); const startTime = new Date(yesterday.setHours(0, 0, 0, 0)); const endTime = new Date(yesterday.setHours(23, 59, 59, 999)); const query = cloudDb.CloudDBZoneQuery.where(HeartRateData) .equalTo('userId', userId) .greaterThanOrEqualTo('timestamp', startTime) .lessThanOrEqualTo('timestamp', endTime); // 执行查询 const snapshot = await cloudDbZone.executeQuery(query); const hrDataList = snapshot.getSnapshotObjects(); // 计算统计值 if (hrDataList.length === 0) return null; const values = hrDataList.map(data => data.heartRate); const stats = { date: startTime.toISOString().split('T')[0], // YYYY-MM-DD min: Math.min(...values), max: Math.max(...values), avg: Math.round(values.reduce((a, b) => a + b, 0) / values.length), resting: calculateRestingHr(values), // 假设有计算静息心率的方法 userId: userId }; return stats; }; // 5. 将统计结果保存到新的报告对象类型 (HealthReport) 中 const saveReport = async (report) => { if (!report) return; const reportObj = new HealthReport(report); await cloudDbZone.upsert(HealthReport.getObjectTypeName(), [reportObj]); console.log(`为用户 ${report.userId} 生成 ${report.date} 日报成功`); }; // 6. 假设这里有一个目标用户列表 (实际应用中需动态获取) const targetUsers = ['user123', 'user456']; for (const userId of targetUsers) { const report = await calculateStats(userId); await saveReport(report); } return { message: '健康日报生成任务完成' }; }; // 假设的 HealthReport 模型 (需在CloudDB中定义对应ObjectType) class HealthReport { constructor({ date, min, max, avg, resting, userId }) { this.reportDate = date; this.minHeartRate = min; this.maxHeartRate = max; this.avgHeartRate = avg; this.restingHeartRate = resting; this.userId = userId; this.generatedAt = new Date(); } static getObjectTypeName() { return 'HealthReport'; } }
逻辑说明:
- 定时触发: 云函数配置为每天凌晨触发 (
timer
触发器)。 - 数据查询: 查询特定用户前一天的所有心率数据。
- 统计分析: 计算最小值、最大值、平均值和静息心率(静息心率算法通常需要更复杂的逻辑,如取晨起特定时段最低值)。
- 报告存储: 将计算结果存储到
HealthReport
对象类型中,供客户端查询展示。
五、成就系统与排行榜 (ArkTS + Cloud DB 聚合查询)
1. 查询用户今日运动数据 (步数示例)
// pages/Achievements.ets import { HealthReport, HeartRateData } from '../model'; // 引入模型 import { cloudDbZone } from '../utils/CloudDBManager'; // 引入初始化好的cloudDbZone @State dailyStats: HealthReport | null = null; // 日报数据 @State stepCount: number = 0; // 今日步数 (假设从步数传感器获取) // 1. 查询用户今日健康报告 async function fetchTodayReport(): Promise<void> { try { const currentUser = getCurrentUser(); if (!currentUser) return; const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD const query = cloudDb.CloudDBZoneQuery.where(HealthReport) .equalTo('userId', currentUser.uid) .equalTo('reportDate', today); const snapshot = await cloudDbZone.executeQuery(query); const reports = snapshot.getSnapshotObjects(); if (reports.length > 0) { this.dailyStats = reports[0]; // 取最新的一条 } } catch (err) { console.error('获取日报失败:', err); } } // 2. 勋章计算逻辑 (示例:连续7天达标) checkConsistencyMedal(): void { if (this.dailyStats && this.dailyStats.restingHeartRate < 60) { // 更新本地连续达标天数 this.consecutiveDays++; if (this.consecutiveDays >= 7) { // 授予勋章! (更新用户档案或展示) console.log('恭喜获得"心率稳定之星"勋章!'); } } else { this.consecutiveDays = 0; // 中断则重置 } } // 3. 获取步数排行榜 (前10名) @State leaderboard: { userId: string, steps: number, userName?: string }[] = []; async function fetchStepLeaderboard(): Promise<void> { try { // 假设有一个存储每日步数汇总的对象类型 DailyStepSummary const query = cloudDb.CloudDBZoneQuery.where(DailyStepSummary) .equalTo('date', new Date().toISOString().split('T')[0]) // 查今天 .orderByDesc('totalSteps') // 按步数降序 .limit(10); // 前10名 const snapshot = await cloudDbZone.executeQuery(query); const topList = snapshot.getSnapshotObjects(); // 可能需要根据 userId 查询用户名 (需集成用户信息服务) this.leaderboard = topList.map(item => ({ userId: item.userId, steps: item.totalSteps, // userName: await getUserName(item.userId) // 异步获取用户名 })); } catch (err) { console.error('获取排行榜失败:', err); } }
UI 渲染示例 (简化):
build() { Column() { // 显示今日报告卡片 if (this.dailyStats) { Card() { Text(`今日心率报告 (${this.dailyStats.reportDate})`) .fontSize(18) .margin(10) Row() { Text(`平均: ${this.dailyStats.avgHeartRate}`) Text(`最低: ${this.dailyStats.minHeartRate}`) Text(`最高: ${this.dailyStats.maxHeartRate}`) Text(`静息: ${this.dailyStats.restingHeartRate}`) }.justifyContent(FlexAlign.SpaceAround) } } // 显示勋章 if (this.hasMedal) { Image($r('app.medal.heart_steady')) // 勋章图片资源 .width(80) .height(80) } // 显示步数排行榜 List({ space: 5 }) { ForEach(this.leaderboard, (item, index) => { ListItem() { Row() { Text(`${index + 1}. `) // Text(item.userName || '用户' + item.userId.slice(0, 4)) // 显示用户名或部分ID Text(`步数: ${item.steps}`).fontColor(Color.Blue) }.padding(10) } }) } } }
功能说明:
- 报告展示 (
fetchTodayReport
): 从 Cloud DB 查询并展示当日生成的健康报告。 - 勋章系统 (
checkConsistencyMedal
): 基于业务逻辑(如连续7天静息心率达标)判断是否授予勋章。勋章状态可存储在用户档案或本地。 - 排行榜 (
fetchStepLeaderboard
): 利用 Cloud DB 的排序 (orderByDesc
) 和限制 (limit
) 功能,高效获取步数排行榜前10名数据。用户名可能需要额外查询用户信息服务。
六、总结与最佳实践
通过本案例,我们实现了基于 HarmonyOS Next 的运动健康应用核心功能:
- 精准数据采集: 利用 HarmonyOS 传感器框架安全、高效获取心率等健康数据。
- 可靠云端同步: 通过 AppGallery Connect 云数据库实现用户数据的持久化存储和跨设备同步。
- 智能数据分析: 利用云函数在后台执行复杂的数据聚合与报告生成任务,减轻客户端负担。
- 互动激励体系: 结合勋章和排行榜,提升用户参与度和运动积极性。
最佳实践建议:
- 权限最小化: 仅申请应用必需的健康数据权限,并在使用场景中清晰告知用户。
- 数据缓存与重试: 在网络不稳定时,实现本地数据缓存和云端同步重试机制。
- 电量优化: 传感器使用遵循生命周期管理,及时停止不必要的监听。
- 隐私保护: 敏感健康数据在传输和存储时必须加密 (AppGallery Connect 已提供存储加密),严格遵守隐私政策。
- 云函数优化: 对大数据量操作进行分页处理,设置合理的超时时间和内存配置。
- UI 体验: 健康数据展示要清晰易懂,使用图表直观呈现趋势变化。
本案例提供了一个坚实的开发框架,开发者可在此基础上扩展睡眠监测、运动模式识别、个性化建议等更高级功能,构建出功能全面、用户体验卓越的运动健康应用。