HarmonyOS 运动健康服务:让你的应用读写运动数据

什么是运动健康服务

你有没有用过华为运动健康 App 记录跑步数据?比如今天跑了 5 公里、消耗了 300 卡路里、平均心率 140。这些数据都存在华为的健康数据平台里。

Health Service Kit(运动健康服务)就是让开发者也能读写这些数据的工具。你不需要自己搭数据存储,不需要自己做数据同步,只要用户授权了,你的 App 就能往华为的健康数据平台里读写运动记录。

举个例子:你做了一个跑步 App,用户跑完步后,你把跑步数据(距离、时间、心率等)写入华为的健康平台。然后用户打开华为运动健康 App,就能看到你 App 记录的跑步数据了。反过来也行,用户在华为运动健康里记录的跑步数据,你的 App 也能读取到。

和前面讲的 Health Industry SDK 不同,Health Service Kit 面向的是普通应用开发者,不需要穿戴设备,只要用户有华为账号就行。Health Industry SDK 是给医疗行业用的,需要穿戴设备采集数据;Health Service Kit 是给普通 App 用的,主要做运动数据的读写。

核心功能

Health Service Kit 提供以下功能:

  1. 用户授权:获取用户授权,才能读写数据。这个是必须的,没授权的话什么都干不了。就像你去别人家,得先敲门得到允许才能进去
  2. 保存锻炼记录:往用户的健康数据里写入运动记录。比如用户跑完步,你把跑步的距离、时间、卡路里、心率等数据存进去。这些数据会同步到华为运动健康 App 里
  3. 读取锻炼记录:从用户的健康数据里读取运动记录。比如你想展示用户最近一周的运动数据,就用这个功能去读取。读出来的数据包括摘要和详细数据
  4. 删除锻炼记录:删除指定的运动记录。比如用户在你的 App 里删除了某次跑步记录,你也得从健康平台里把它删掉,不然数据不一致

环境搭建

硬件要求

  • 设备类型:华为手机、华为平板
  • HarmonyOS 系统:HarmonyOS NEXT Developer Beta1 及以上

软件要求

  • DevEco Studio 版本:DevEco Studio NEXT Developer Beta1 及以上
  • HarmonyOS SDK 版本:HarmonyOS NEXT Developer Beta1 SDK 及以上

搭建步骤

  1. 安装 DevEco Studio:去华为开发者官网下载安装,跟着提示走就行
  2. 配置开发环境:确保网络环境正常,DevEco Studio 需要联网才能用
  3. 设备调试:使用真机进行调试,模拟器也可以
  4. 申请服务:参考官方文档申请运动健康服务。这一步很重要,不申请的话接口调不通

项目结构

├── entry/src/main/ets
│  ├── common
│  │  ├── bean
│  │  │  ├── AuthManagement.ets              // 授权接口
│  │  │  └── ExerciseSequenceManagement.ets  // 锻炼记录接口
│  │  ├── utils
│  │  │  └── DateUtils.ets                   // 时间工具类
│  ├── entryability
│  │  └── EntryAbility.ets                   // 程序入口类
│  ├── pages
│  │  ├── MainIndex.ets                      // 主页面
│  │  ├── AuthIndex.ets                      // 授权页面
│  │  └── ExerciseSequenceIndex.ets          // 锻炼记录页面
└── entry/src/main/resources                 // 资源文件目录

项目结构比较清晰,授权和锻炼记录分别放在不同的文件里。AuthManagement.ets 处理授权逻辑,ExerciseSequenceManagement.ets 处理锻炼记录的增删改查。

运动健康数据读写流程

下面是 Health Service Kit 的完整使用流程:

保存

读取

删除

初始化 Health Service

配置授权参数

请求用户授权

用户是否授权?

获取授权的数据类型

无法读写数据

操作类型

构造锻炼记录

构造查询请求

构造删除请求

调用 saveData

调用 readData

调用 deleteData

数据同步到华为运动健康

返回运动记录列表

记录删除成功

锻炼记录数据结构

下面是锻炼记录的数据结构:

锻炼记录

基本信息

摘要数据

详细数据

数据类型

开始/结束时间

运动类型

运动时长

总距离

总卡路里

平均/最大速度

心率变化

速度变化

海拔变化

第一步:导入模块

import { healthStore } from '@kit.HealthServiceKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';

导入 Health Service Kit 的核心模块:

  • healthStore:运动健康服务的核心接口,所有的读写操作都靠它。包括授权、保存、读取、删除等功能
  • hilog:日志工具,开发调试的时候看输出用的。出了问题也能查日志,建议别删
  • common:AbilityKit 的通用接口,获取上下文用的。调用 Health Service Kit 的接口时需要传入上下文

第二步:初始化 Health Service

在使用 Health Service Kit 之前,需要先初始化。

healthStore.init(context);

调用 healthStore.init 初始化服务。这一步只需要在首次调用时执行一次就行,不用每次都调。

为什么要初始化?因为 Health Service Kit 需要建立和健康数据平台的连接,初始化就是建立这个连接的过程。就像你打电话之前,得先拨号一样。

context 是应用的上下文,一般通过 getContext(this) 获取。

第三步:用户授权

在读写数据之前,需要先获取用户授权。这一步是必须的,没授权的话,后面的读写操作都会失败。

let authorizationParameter: healthStore.AuthorizationRequest = {
  readDataTypes: [healthStore.exerciseSequenceHelper.DATA_TYPE],
  writeDataTypes: [healthStore.exerciseSequenceHelper.DATA_TYPE]
};

配置授权参数:

  • readDataTypes:需要读取的数据类型。这里填的是锻炼记录类型,表示"我要读取用户的锻炼记录"
  • writeDataTypes:需要写入的数据类型。这里也是锻炼记录类型,表示"我要往用户的健康数据里写入锻炼记录"

你可以只申请读权限,也可以只申请写权限,也可以两个都申请。根据你的实际需求来。

try {
  let authorizationResponse = await healthStore.requestAuthorizations(
    getContext(this) as common.UIAbilityContext, 
    authorizationParameter
  );
  console.info('Succeeded in requesting authorization.');
  authorizationResponse.writeDataTypes.forEach(dataType => {
    console.info(`grantedWriteDataType is: ${dataType.name}`);
  });
  authorizationResponse.readDataTypes.forEach(dataType => {
    console.info(`grantedReadDataTypes is: ${dataType.name}`);
  });
} catch (err) {
  console.error(`Failed to request authorization. Code: ${err.code}, message: ${err.message}`);
}

调用 healthStore.requestAuthorizations 请求授权。这会弹出一个授权页面,让用户选择允许哪些权限。

授权页面长什么样?就是一个系统弹窗,上面列出了你的应用需要的权限,用户可以点"允许"或"拒绝"。用户点了"允许"之后,你的应用才能读写数据。

如果用户已经授权过,就不会再弹出授权页面了,直接返回之前授权的结果。这样用户体验好一些,不用每次都弹窗。

授权成功后,authorizationResponse 里包含了用户实际授权的数据类型。你可以检查一下,用户是不是都授权了。如果用户只授权了读权限没授权写权限,你就不能写数据。

第四步:保存锻炼记录

用户授权后,就可以保存锻炼记录了。这是最常用的功能。

const startTime = 1698040800000; // 2023-10-23 14:00:00
const endTime = 1698042600000;   // 2023-10-23 14:30:00

const runningSequence: healthStore.exerciseSequenceHelper.running.Model = {
  dataType: healthStore.exerciseSequenceHelper.DATA_TYPE,
  dataSourceId: ExerciseSequenceManagement.dataSourceId,
  startTime: startTime,
  endTime: endTime,
  localDate: DateUtil.parseYyyyMmDdToDate(DateUtil.getYyyyMmDd(1698040800000)),
  timeZone: '+0800',
  modifiedTime: new Date().getTime(),
  exerciseType: healthStore.exerciseSequenceHelper.running.EXERCISE_TYPE,
  duration: 1800,
  summaries: {
    distance: {
      totalDistance: 2000
    },
    calorie: {
      totalCalories: 20
    },
    speed: {
      avg: 5,
      max: 6
    }
  },
  details: {
    exerciseHeartRate: [
      { startTime: startTime, bpm: 88 },
      { startTime: startTime + 5000, bpm: 89 }
    ],
    speed: [
      { startTime: startTime, speed: 2.5 },
      { startTime: startTime + 5000, speed: 2.3 }
    ],
    altitude: [
      { startTime: startTime, altitude: 100 },
      { startTime: startTime + 5000, altitude: 101 }
    ],
  }
};

构造跑步记录数据,这个对象有点长,我一个一个解释:

基本信息

  • dataType:数据类型,告诉系统"这是锻炼记录"
  • dataSourceId:数据源 ID,标识是哪个应用写入的数据。每个应用有自己的数据源 ID
  • startTimeendTime:运动的开始和结束时间,单位是毫秒。1698040800000 是 2023-10-23 14:00:00 的时间戳
  • localDate:本地日期,用工具类把时间戳转成日期格式
  • timeZone:时区,+0800 表示东八区(北京时间)
  • modifiedTime:修改时间,用当前时间

运动信息

  • exerciseType:运动类型,这里是跑步。Health Service Kit 支持多种运动类型,比如跑步、骑行、游泳等
  • duration:运动时长,1800 秒 = 30 分钟

摘要数据(summaries)

  • distance.totalDistance:总距离,2000 米 = 2 公里
  • calorie.totalCalories:总卡路里,20 卡路里
  • speed.avgspeed.max:平均速度和最大速度,单位是 m/s

详细数据(details)

  • exerciseHeartRate:心率变化记录,每 5 秒记录一次心率
  • speed:速度变化记录,每 5 秒记录一次速度
  • altitude:海拔变化记录,每 5 秒记录一次海拔

详细数据是可选的,你可以不填。但如果你填了,用户在华为运动健康 App 里就能看到心率曲线、速度曲线等详细图表。

try {
  await healthStore.saveData(runningSequence);
  console.info('Succeeded in saving data.');
} catch (err) {
  console.error(`Failed to save data. Code: ${err.code}, message: ${err.message}`);
}

调用 healthStore.saveData 保存跑步记录。保存成功后,数据会自动同步到华为运动健康 App 里。

如果保存失败,可能是以下原因:用户没授权、数据格式不对、网络问题等。要做好错误处理。

第五步:读取锻炼记录

保存之后,可以读取锻炼记录。

const sequenceReadRequest: healthStore.ExerciseSequenceReadRequest<healthStore.exerciseSequenceHelper.running.DetailFields> = {
  startTime: 1698040800000,
  endTime: 1698042600000,
  exerciseType: healthStore.exerciseSequenceHelper.running.EXERCISE_TYPE,
  count: 1,
  sortOrder: 1,
  readOptions: {
    withPartialDetails: ['exerciseHeartRate', 'altitude']
  }
};

构造查询请求:

  • startTimeendTime:查询的时间范围。只返回这个时间段内的运动记录
  • exerciseType:运动类型,只查询跑步记录。如果你想查所有类型的运动,可以不填这个字段
  • count:返回的记录数量。设为 1 表示只返回 1 条记录。如果你想返回多条,可以设大一点
  • sortOrder:排序方式。1 表示按时间排序
  • readOptions.withPartialDetails:需要返回哪些详细数据。这里指定了心率和海拔,表示"我只要心率和海拔的详细数据,其他的不要"

为什么要指定 withPartialDetails?因为详细数据可能很大,如果你只需要心率数据,就没必要把速度、海拔等数据都拉回来,浪费流量和时间。

try {
  const runningSequences = await healthStore.readData<healthStore.exerciseSequenceHelper.running.Model>(sequenceReadRequest);
  
  console.info('Succeeded in reading data.');
  runningSequences.forEach((runningSequence) => {
    console.info(`the start time is ${runningSequence.startTime}.`);
    console.info(`the end time is ${runningSequence.endTime}.`);
    Object.keys(runningSequence.summaries).forEach((key) => {
      Object.keys(runningSequence.summaries[key]).forEach((fieldName) => {
        console.info(`the summaries of ${key} field ${fieldName} is ${runningSequence.summaries[key][fieldName]}.`);
      });
    });
  });
} catch (err) {
  console.error(`Failed to read data. Code: ${err.code}, message: ${err.message}`);
}

调用 healthStore.readData 查询跑步记录。返回的结果是一个数组,包含了所有符合条件的运动记录。

遍历每条记录,打印出开始时间、结束时间、摘要数据等信息。Object.keys 是用来遍历对象的键,这样就能把所有的摘要数据都打印出来。

读取出来的数据你可以展示在页面上,比如做成运动记录列表、运动趋势图表等。

第六步:删除锻炼记录

如果需要删除某次锻炼记录,有两种方式。

方式一:指定记录删除

const runningSequence: healthStore.exerciseSequenceHelper.running.Model = {
  dataType: healthStore.exerciseSequenceHelper.DATA_TYPE,
  dataSourceId: ExerciseSequenceManagement.dataSourceId,
  startTime: startTime,
  endTime: endTime,
  localDate: DateUtil.parseYyyyMmDdToDate(DateUtil.getYyyyMmDd(1698040800000)),
  timeZone: '+0800',
  modifiedTime: new Date().getTime(),
  exerciseType: healthStore.exerciseSequenceHelper.running.EXERCISE_TYPE,
  duration: 1800,
  summaries: {
    distance: { totalDistance: 0 },
    calorie: { totalCalories: 0 },
    speed: { avg: 0, max: 0 }
  },
};

try {
  await healthStore.deleteData(runningSequence);
  console.info('Succeeded in deleting data.');
} catch (err) {
  console.error(`Failed to delete data. Code: ${err.code}, message: ${err.message}`);
}

构造要删除的记录,然后调用 healthStore.deleteData 删除。构造的方式和保存时类似,但摘要数据可以填 0,因为删除的时候不需要这些数据。

这种方式适合"用户在你的 App 里删除了某条记录"的场景。你需要先知道要删除哪条记录,然后构造对应的对象去删除。

方式二:按请求删除

let exerciseSequenceDeleteRequest: healthStore.ExerciseSequenceDeleteRequest = {
  exerciseType: healthStore.exerciseSequenceHelper.running.EXERCISE_TYPE,
  startTime: 1698633801000,
  endTime: 1698633801000
};

try {
  await healthStore.deleteData(exerciseSequenceDeleteRequest);
  console.info('Succeeded in deleting data.');
} catch (err) {
  console.error(`Failed to delete data. Code: ${err.code}, message: ${err.message}`);
}

按时间范围删除,删除指定时间段内的所有记录。这种方式更简单,不需要构造完整的记录对象,只需要指定运动类型和时间范围就行。

适合"批量删除"的场景,比如用户想删除某一天的所有运动记录。

实际应用场景

Health Service Kit 在实际开发中有很多用途:

运动记录应用

// 记录用户的跑步数据
async function saveRunningData(distance: number, duration: number, calories: number) {
  const runningSequence: healthStore.exerciseSequenceHelper.running.Model = {
    dataType: healthStore.exerciseSequenceHelper.DATA_TYPE,
    dataSourceId: 'my_app_data_source',
    startTime: Date.now() - duration * 1000,
    endTime: Date.now(),
    exerciseType: healthStore.exerciseSequenceHelper.running.EXERCISE_TYPE,
    duration: duration,
    summaries: {
      distance: { totalDistance: distance },
      calorie: { totalCalories: calories },
    }
  };
  await healthStore.saveData(runningSequence);
}

运动记录应用是最常见的场景。用户跑完步后,你把跑步数据保存到健康平台。数据会自动同步到华为运动健康 App,用户在那边也能看到。

运动数据分析

// 分析用户的运动趋势
async function analyzeWorkoutTrend(startTime: number, endTime: number) {
  const request: healthStore.ExerciseSequenceReadRequest = {
    startTime: startTime,
    endTime: endTime,
    exerciseType: healthStore.exerciseSequenceHelper.running.EXERCISE_TYPE,
    count: 100,
  };
  const data = await healthStore.readData(request);
  // 分析运动趋势,比如每周跑量、配速变化等
  return analyzeTrend(data);
}

运动数据分析应用可以读取用户的历史运动数据,然后做各种分析。比如分析每周的跑量趋势、配速变化、心率变化等。这些分析结果可以帮助用户更好地了解自己的运动状态。

健身打卡

// 记录用户的健身打卡
async function checkIn(workoutType: string, duration: number) {
  const workout = createWorkoutRecord(workoutType, duration);
  await healthStore.saveData(workout);
  showSuccess('打卡成功!');
}

健身打卡应用可以记录用户的健身数据。用户每次健身完,点一下打卡按钮,数据就保存到健康平台了。用户可以在华为运动健康 App 里看到自己的打卡记录。

适用场景

Health Service Kit 适合以下场景:

  • 运动应用:记录和分析运动数据
  • 健身应用:健身打卡、训练记录
  • 健康应用:健康数据管理
  • 企业应用:员工健康管理
  • 社交应用:运动排行榜

注意事项

  1. 用户授权:必须先获取用户授权才能读写数据,这是强制要求
  2. 数据类型:要指定正确的数据类型,否则数据可能保存不了或者读取不到
  3. 时间戳:时间戳是毫秒级的,不是秒级的。搞错了时间戳,数据的时间就不对了
  4. 数据源 ID:写入数据需要先获取数据源 ID,每个应用有自己的 ID
  5. 网络要求:需要联网才能完成授权,数据同步也需要网络

总结

Health Service Kit 让你的应用可以读写用户的运动健康数据,核心流程:

  1. 初始化 Health Service,建立和健康平台的连接
  2. 获取用户授权,让用户允许你读写数据
  3. 保存锻炼记录,把运动数据写入健康平台
  4. 读取锻炼记录,从健康平台读取运动数据
  5. 删除锻炼记录,从健康平台删除指定的运动数据

掌握了这些,你就能开发出各种运动健康应用,帮助用户更好地管理自己的运动数据。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐