写入数据

本指南将介绍在 Health Connect 中写入或更新数据的过程。

设置数据结构

在写入数据之前,我们需要先设置记录。超过 50 种数据类型各有自己的结构。如需详细了解可用的数据类型,请查看 Jetpack 参考文档

基本记录

Health Connect 中的步数数据类型会记录用户在两次读取数据之间完成的步数。步数是各健身和健康平台的常见测量内容。

以下示例展示了如何设置步数数据:

val stepsRecord = StepsRecord(
    count = 120,
    startTime = START_TIME,
    endTime = END_TIME,
    startZoneOffset = START_ZONE_OFFSET,
    endZoneOffset = END_ZONE_OFFSET
)

包含测量单位的记录

Health Connect 可存储值及其测量单位以提供准确数据。例如,内容广泛而全面的营养数据类型,它包含各种可选的营养素字段,从碳水化合物总量到维生素,不一而足。每个数据点都代表着可能作为膳食或食物的一部分被人摄入的营养素。

在此数据类型中,所有营养素都用 Mass 单位表示,而 energy 则用 Energy 单位表示。

以下示例展示了如何为吃了一根香蕉的用户设置营养数据:

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 = START_TIME,
    endTime = END_TIME,
    startZoneOffset = START_ZONE_OFFSET,
    endZoneOffset = END_ZONE_OFFSET
)

包含系列数据的记录

Health Connect 可以存储系列数据的列表。例如,心率数据类型可捕获在两次读数之间检测到的一系列心跳数据样本。

在此数据类型中,参数 samples 由一系列心率样本表示。每个样本都包含一个 beatsPerMinute 值和一个 time 值。

以下示例展示了如何设置心率系列数据:

val heartRateRecord = HeartRateRecord(
    startTime = START_TIME,
    startZoneOffset = START_ZONE_OFFSET,
    endTime = END_TIME,
    endZoneOffset = END_ZONE_OFFSET,
    // records 10 arbitrary data, to replace with actual data
    samples = List(10) { index ->
        HeartRateRecord.Sample(
            time = START_TIME + Duration.ofSeconds(index.toLong()),
            beatsPerMinute = 100 + index.toLong(),
        )
    }
)

写入数据

Health Connect 中的常见工作流之一是写入数据。如需添加记录,请使用 insertRecords

以下示例展示了如何通过插入步数来写入数据:

suspend fun insertSteps(healthConnectClient: HealthConnectClient) {
    try {
        val stepsRecord = StepsRecord(
            count = 120,
            startTime = START_TIME,
            endTime = END_TIME,
            startZoneOffset = START_ZONE_OFFSET,
            endZoneOffset = END_ZONE_OFFSET
        )
        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)
        }

        client.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(
        // Assign parameters for this record
        metadata = Metadata(
            clientRecordId = cid
        )
    )
    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 sr = StepsRecord(
    count = count,
    startTime = startTime,
    startZoneOffset = startZoneOffset,
    endTime = endTime,
    endZoneOffset = endZoneOffset,
    metadata = Metadata(
        clientRecordId = cid,
        clientRecordVersion = version
    )
)

每当发生变化时,更新/插入操作不会自动增大 version,从而防止发生意外覆盖数据的情况。因此,您必须手动为其提供更高的值。

最佳实践

构建逻辑后,请考虑在写入或更新数据时遵循最佳实践