本指南与 Health Connect 版本 1.1.0-alpha12 兼容。
本指南将介绍在 Health Connect 中写入或更新数据的过程。
设置数据结构
在写入数据之前,我们需要先设置记录。超过 50 种数据类型各有自己的结构。如需详细了解可用的数据类型,请查看 Jetpack 参考文档。
基本记录
Health Connect 中的步数数据类型会记录用户在两次读取数据之间完成的步数。步数是各健身和健康平台的常见测量内容。
以下示例展示了如何设置步数数据:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
包含测量单位的记录
Health Connect 可存储值及其测量单位以提供准确数据。例如,内容广泛而全面的营养数据类型,它包含各种可选的营养素字段,从碳水化合物总量到维生素,不一而足。每个数据点都代表着可能作为膳食或食物的一部分被人摄入的营养素。
在此数据类型中,所有营养素都用 Mass
单位表示,而 energy
则用 Energy
单位表示。
以下示例展示了如何为吃了一根香蕉的用户设置营养数据:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(1))
val banana = NutritionRecord(
name = "banana",
energy = 105.0.kilocalories,
dietaryFiber = 3.1.grams,
potassium = 0.422.grams,
totalCarbohydrate = 27.0.grams,
totalFat = 0.4.grams,
saturatedFat = 0.1.grams,
sodium = 0.001.grams,
sugar = 14.0.grams,
vitaminB6 = 0.0005.grams,
vitaminC = 0.0103.grams,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
device = Device(type = Device.TYPE_PHONE)
)
)
包含系列数据的记录
Health Connect 可以存储系列数据的列表。例如,心率数据类型可捕获在两次读数之间检测到的一系列心跳数据样本。
在此数据类型中,参数 samples
由一系列心率样本表示。每个样本都包含一个 beatsPerMinute
值和一个 time
值。
以下示例展示了如何设置心率系列数据:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
val heartRateRecord = HeartRateRecord(
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
// records 10 arbitrary data, to replace with actual data
samples = List(10) { index ->
HeartRateRecord.Sample(
time = startTime + Duration.ofSeconds(index.toLong()),
beatsPerMinute = 100 + index.toLong(),
)
},
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
))
写入数据
Health Connect 中的常见工作流之一是写入数据。如需添加记录,请使用 insertRecords
。
以下示例展示了如何通过插入步数来写入数据:
suspend fun insertSteps(healthConnectClient: HealthConnectClient) {
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
try {
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
healthConnectClient.insertRecords(listOf(stepsRecord))
} catch (e: Exception) {
// Run error handling here
}
}
更新数据
如果您需要更改一条或多条记录,尤其是在需要将应用数据存储区与 Health Connect 中的数据同步时,您可以更新数据。您可通过以下两种方式之一来更新现有数据,具体取决于用来查找记录的标识符。
元数据
应考虑先检查 Metadata
类,因为在更新数据前有必要这样做。创建后,Health Connect 中的每个 Record
都有一个 metadata
字段。以下属性与同步相关:
属性 | 说明 |
---|---|
id
|
Health Connect 中的每个 Record 都有一个唯一的 id 值。插入新记录时,Health Connect 会自动填充此信息。 |
lastModifiedTime
|
每个 Record 还会记录自身的上次修改时间。Health Connect 会自动填充此信息。 |
clientRecordId
|
每个 Record 都可以有一个与其关联的唯一 ID,以便在应用数据存储区中用作参考。您的应用会提供此值。 |
clientRecordVersion
|
如果记录具有 clientRecordId ,则 clientRecordVersion 可用于允许数据与应用数据存储区中的版本保持同步。您的应用会提供此值。 |
通过记录 ID 进行更新
如需更新数据,请先准备好所需的记录。对记录执行所有必要的更改。然后,调用 updateRecords
进行更改。
以下示例展示了如何更新数据。为此,每条记录的时区偏移量值调整为 PST。
suspend fun updateSteps(
healthConnectClient: HealthConnectClient,
prevRecordStartTime: Instant,
prevRecordEndTime: Instant
) {
try {
val request = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = StepsRecord::class, timeRangeFilter = TimeRangeFilter.between(
prevRecordStartTime, prevRecordEndTime
)
)
)
val newStepsRecords = arrayListOf<StepsRecord>()
for (record in request.records) {
// Adjusted both offset values to reflect changes
val sr = StepsRecord(
count = record.count,
startTime = record.startTime,
startZoneOffset = record.startTime.atZone(ZoneId.of("PST")).offset,
endTime = record.endTime,
endZoneOffset = record.endTime.atZone(ZoneId.of("PST")).offset,
metadata = record.metadata
)
newStepsRecords.add(sr)
}
healthConnectClient.updateRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
通过客户端记录 ID 进行更新/插入
如果您使用了可选的客户端记录 ID 和客户端记录版本值,我们建议您使用 insertRecords
而非 updateRecords
。
insertRecords
函数能更新/插入数据。如果数据是基于给定的一组客户端记录 ID 而存在于 Health Connect 中,则会被覆盖。否则,会被作为新数据写入。当您需要将应用数据存储区中的数据同步到 Health Connect 时,上述方案非常有用。
以下示例展示了如何对从应用数据存储区中提取的数据执行更新/插入操作:
suspend fun pullStepsFromDatastore() : ArrayList<StepsRecord> {
val appStepsRecords = arrayListOf<StepsRecord>()
// Pull data from app datastore
// ...
// Make changes to data if necessary
// ...
// Store data in appStepsRecords
// ...
var sr = StepsRecord(
metadata = Metadata.autoRecorded(
clientRecordId = "Your client record ID",
device = Device(type = Device.TYPE_WATCH)
),
// Assign more parameters for this record
)
appStepsRecords.add(sr)
// ...
return appStepsRecords
}
suspend fun upsertSteps(
healthConnectClient: HealthConnectClient,
newStepsRecords: ArrayList<StepsRecord>
) {
try {
healthConnectClient.insertRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
然后,您可在主线程中调用这些函数。
upsertSteps(healthConnectClient, pullStepsFromDatastore())
客户端记录版本中的值检查
如果更新/插入数据的过程包含客户端记录版本,Health Connect 会对 clientRecordVersion
值执行比较检查。如果所插数据的版本高于现有数据的版本,就会发生更新/插入。否则,该过程会忽略此更改,并且值将保持不变。
如果想在数据中包含版本编号,您需要根据版本编号逻辑为 Metadata.clientRecordVersion
提供 Long
值。
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 100L,
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
clientRecordId = "Your supplied record ID",
clientRecordVersion = 0L, // Your supplied record version
device = Device(type = Device.TYPE_WATCH)
)
)
每当发生变化时,更新/插入操作不会自动增大 version
,从而防止发生意外覆盖数据的情况。因此,您必须手动为其提供更高的值。
写入数据的最佳实践
应用只能将自身来源的数据写入 Health Connect。
如果您应用中的数据是从另一应用导入的,则需由另一应用负责将其自身的数据写入 Health Connect。
此外,最好能实现用于处理写入异常(例如数据超出边界或内部系统出错)的逻辑。您可对作业调度机制应用退避和重试策略。如果最终无法向 Health Connect 写入数据,请确保应用可以越过该导出点。别忘了要记录并报告错误以帮助诊断。
跟踪数据时,您可遵循几条建议,具体取决于应用写入数据的方式。
被动跟踪
以下建议适用于执行被动健身或健康跟踪的应用,例如在后台持续记录步数或心率的应用。
您的应用需要通过以下方式定期将数据写入 Health Connect:
- 每次同步时,仅写入新数据,并更新自上次同步后发生修改的数据。
- 对请求进行分块,且每个写入请求的记录数量不超过 1,000 条。
- 使用
WorkManager
安排周期性后台任务(时限至少为 15 分钟)。 将任务限制为仅在设备空闲且电池电量充足时运行。
val constraints = Constraints.Builder() .requiresBatteryNotLow() .requiresDeviceIdle(true) .build() val writeDataWork = PeriodicWorkRequestBuilder<WriteDataToHealthConnectWorker>( 15, TimeUnit.MINUTES, 5, TimeUnit.MINUTES ) .setConstraints(constraints) .build()
主动跟踪
以下建议适用于执行基于事件的跟踪(例如锻炼和睡眠)或手动用户输入(例如营养摄入)的应用。应用在前台运行或出现应用一天只被使用寥寥几次的罕见事件时,会创建此类记录。
确保应用不会在事件的整个持续期间使 Health Connect 始终保持运行状态。
必须通过以下两种方式之一将数据写入 Health Connect:
- 在事件完成后将数据同步到 Health Connect。例如,在用户结束一个被跟踪的锻炼时段时同步数据。
- 使用
WorkManager
安排一次性任务,以便日后需要时同步数据。
写入精细程度和频率的最佳实践
将数据写入 Health Connect 时,请使用适当的分辨率。使用合适的分辨率有助于减少存储负载,同时仍能保持数据的一致性和准确性。数据解析包含以下 2 个方面:
- 写入频率:应用将任何新数据推送到 Health Connect 的频率。例如,每 15 分钟写入一次新数据。
- 写入数据的粒度:推送的数据的采样频率。例如,每 5 秒写入一次心率样本。并非所有数据类型都需要相同的采样率。例如,每秒更新步数数据几乎没什么好处,不如降低更新频率(例如每 60 秒一次)。不过,如果采样率较高,用户就可以更详尽、更精细地了解自己的健康与健身数据。采样频率应能够在细节和性能之间取得平衡。
写入全天监控的数据
对于持续收集的数据(例如步数),您的应用应在一天内至少每 15 分钟写入一次 Health Connect。
数据类型 |
单位 |
预期 |
示例 |
步骤 |
steps |
每隔 1 分钟 |
23:14 - 23:15 - 5 步 23:16 - 23:17 - 22 步 23:17 - 23:18 - 8 步 |
StepsCadence |
步/分钟 |
每隔 1 分钟 |
23:14 - 23:15 - 5 spm 23:16 - 23:17 - 22 spm 23:17 - 23:18 - 8 spm |
推轮椅次数 |
推送 |
每隔 1 分钟 |
23:14 - 23:15 - 5 次推送 23:16 - 23:17 - 22 次推送 23:17 - 23:18 - 8 次推送 |
ActiveCaloriesBurned |
卡路里 |
每隔 15 分钟 |
23:15 - 23:30 - 2 卡路里 23:30 - 23:45 - 25 卡路里 23:45 - 00:00 - 5 卡路里 |
TotalCaloriesBurned |
卡路里 |
每隔 15 分钟 |
23:15 - 23:30 - 16 卡路里 23:30 - 23:45 - 16 卡路里 23:45 - 00:00 - 16 卡路里 |
距离 |
公里/分钟 |
每隔 1 分钟 |
23:14-23:15 - 0.008 公里 23:16 - 23:16 - 0.021 公里 23:17 - 23:18 - 0.012 公里 |
ElevationGained |
m |
每隔 1 分钟 |
20:36 - 20:37 - 3.048m 20:39 - 20:40 - 3.048m 23:23 - 23:24 - 9.144m |
FloorsClimbed |
层 |
每隔 1 分钟 |
23:14 - 23:15 - 5 层 23:16 - 23:16 - 22 层 23:17 - 23:18 - 8 层 |
HeartRate |
bpm |
每隔 1 分钟 |
6:11am - 55 bpm |
HeartRateVariabilityRmssd |
毫秒 |
每隔 1 分钟 |
6:11am - 23 ms |
RespiratoryRate |
每分钟呼吸次数 |
每隔 1 分钟 |
23:14 - 23:15 - 每分钟 60 次呼吸 23:16 - 23:16 - 每分钟 62 次呼吸 23:17 - 23:18 - 每分钟 64 次呼吸 |
OxygenSaturation |
% |
每隔 1 小时 |
6:11 - 95.208% |
写入时段
应在锻炼或睡眠时段结束时将数据写入 Health Connect。
最佳实践是,应使用录制设备和适当的元数据(包括 RecordingMethod
)写入任何睡眠时段或锻炼时段。
您的应用至少应遵循下方“预期”列中的指南。请尽可能遵循“最佳”指南。
锻炼期间跟踪的数据
数据类型 |
单位 |
预期 |
祝好 |
示例 |
步骤 |
steps |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 5 步 23:16 - 23:17 - 22 步 23:17 - 23:18 - 8 步 |
StepsCadence |
步/分钟 |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 35 spm 23:16 - 23:17 - 37 spm 23:17 - 23:18 - 40 spm |
推轮椅次数 |
推送 |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 5 次推送 23:16 - 23:17 - 22 次推送 23:17 - 23:18 - 8 次推送 |
CyclingPedalingCadence |
rpm |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 65 rpm 23:16 - 23:17 - 70 rpm 23:17 - 23:18 - 68 rpm |
电源 |
瓦特 |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 250 瓦 23:16 - 23:17 - 255 瓦 23:17 - 23:18 - 245 瓦特 |
速度 |
公里/分钟 |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 0.3 公里/分钟 23:16 - 23:17 - 0.4 公里/分钟 23:17 - 23:18 -0.4 公里/分钟 |
距离 |
公里/米 |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 0.008 公里 23:16 - 23:16 - 0.021 公里 23:17 - 23:18 - 0.012 公里 |
ActiveCaloriesBurned |
卡路里 |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 20 卡路里 23:16 - 23:17 - 20 卡路里 23:17 - 23:18 - 25 卡路里 |
TotalCaloriesBurned |
卡路里 |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 36 卡路里 23:16 - 23:17 - 36 卡路里 23:17 - 23:18 - 41 卡路里 |
ElevationGained |
m |
每隔 1 分钟 |
每 1 秒 |
20:36 - 20:37 - 3.048m 20:39 - 20:40 - 3.048m 23:23 - 23:24 - 9.144m |
ExerciseRoutes |
lat/lng/alt |
每 3-5 秒 |
每 1 秒 |
|
HeartRate |
bpm |
每隔 1 分钟 |
每 1 秒 |
23:14-23:15 - 150 bpm 23:16 - 23:17 -152 bpm 23:17 - 23:18 - 155 bpm |
睡眠期间跟踪的数据
数据类型 |
单位 |
预期样本 |
示例 |
睡眠分期 |
阶段 |
每个睡眠阶段的精细时间段 |
23:46 - 23:50 - 清醒 23:50 - 23:56 - 浅睡眠 23:56 - 00:16 - 深度睡眠 |
RestingHeartRate |
bpm |
单个每日值(预计在早上第一时间提供) |
6:11am - 60 bpm |
OxygenSaturation |
% |
单个每日值(预计在早上第一时间提供) |
6:11 - 95.208% |