基于 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 体验: 健康数据展示要清晰易懂,使用图表直观呈现趋势变化。
本案例提供了一个坚实的开发框架,开发者可在此基础上扩展睡眠监测、运动模式识别、个性化建议等更高级功能,构建出功能全面、用户体验卓越的运动健康应用。

查看15道真题和解析