本指南将介绍在 Health Connect 中写入或更新数据的过程。
处理零值
某些数据类型(例如步数、距离或卡路里)的值可能为 0。只有当用户佩戴设备时确实处于不活动状态时,才写入零值。如果设备未佩戴、数据缺失或电池没电,请勿写入零值。在这种情况下,请省略相应记录,以免数据产生误导。
设置数据结构
在写入数据之前,我们需要先设置记录。超过 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 可存储值及其测量单位以提供准确数据。例如,内容广泛而全面的营养数据类型,它包含各种可选的营养素字段,从碳水化合物总量到维生素,不一而足。每个数据点都代表着可能作为膳食或食物的一部分被人摄入的营养素。
在此数据类型中,所有营养素都用质量单位表示,而 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)
    ))
向用户请求权限
创建客户端实例后,应用需要向用户请求权限。用户必须能够随时授予或拒绝权限。
为此,请为所需的数据类型创建一组权限。 先确保此集中的权限已在 Android 清单中声明。
// Create a set of permissions for required data types
val PERMISSIONS =
    setOf(
  HealthPermission.getReadPermission(HeartRateRecord::class),
  HealthPermission.getWritePermission(HeartRateRecord::class),
  HealthPermission.getReadPermission(StepsRecord::class),
  HealthPermission.getWritePermission(StepsRecord::class)
)
使用 getGrantedPermissions 查看您的应用是否已获得所需的权限。如不具备,请使用 createRequestPermissionResultContract 请求这些权限。系统随即会显示“健康数据共享”权限界面。
// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
  if (granted.containsAll(PERMISSIONS)) {
    // Permissions successfully granted
  } else {
    // Lack of required permissions
  }
}
suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
  val granted = healthConnectClient.permissionController.getGrantedPermissions()
  if (granted.containsAll(PERMISSIONS)) {
    // Permissions already granted; proceed with inserting or reading data
  } else {
    requestPermissions.launch(PERMISSIONS)
  }
}
由于用户可以随时授予或撤消权限,因此您的应用需要定期检查已授予的权限,并应对权限丢失的情况。
写入数据
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 可用于允许数据与应用数据存储区中的版本保持同步。您的应用会提供此值。  | 
按时间范围读取后更新
如需更新数据,请先准备好所需的记录。对记录执行所有必要的更改。然后,调用 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,从而防止发生意外覆盖数据的情况。因此,您必须手动为其提供更高的值。
常规准则
您的应用应写入所有受支持的第一方数据。您可以选择让应用写入从第三方来源获取的数据。不过,如果您的应用已从“健康数据共享”读取数据,则不应将该数据写回“健康数据共享”。
写入已导入或从其他来源派生的数据时,您应正确归因其来源和源设备元数据。为此,您必须为每个写入的记录提供以下元数据:
recordingMethod:对于自动或手动记录的数据,我们希望更新记录方法以反映已记录的活动类型:RECORDING_METHOD_AUTOMATICALLY_RECORDED:如果数据是自动记录的,例如,健身手环自动检测到用户去跑步了。RECORDING_METHOD_ACTIVELY_RECORDED:如果用户开始了一项新活动,例如在可穿戴设备上骑自行车。RECORDING_METHOD_MANUAL_ENTRY:如果用户手动输入了数据。
device.type:您必须指定受支持的Device类型之一的设备类型。device.manufacturer:设备的制造商,例如“Fitbit”。device.model:设备的型号,例如“Charge 3”。
正确设置元数据对于确保数据透明度至关重要,有助于用户了解其健康信息的来源。如需了解完整详情,请参阅健康数据共享元数据指南。
如果您应用中的数据是从另一应用导入的,则需由另一应用负责将其自身的数据写入 Health Connect。
此外,最好能实现用于处理写入异常(例如数据超出边界或内部系统出错)的逻辑。您可对作业调度机制应用退避和重试策略。如果最终无法向 Health Connect 写入数据,请确保应用可以越过该导出点。别忘了要记录并报告错误以帮助诊断。
跟踪数据时,您可遵循几条建议,具体取决于应用写入数据的方式。
时区处理
在写入基于时间的记录时,请避免默认将偏移量设置为 zoneOffset.UTC,因为当用户位于其他时区时,这可能会导致时间戳不准确。而是根据设备的实际位置计算偏移量。您可以使用 ZoneId.systemDefault() 检索设备的时区。
val endTime = Instant.now()
val startTime = endTime.minus(java.time.Duration.ofDays(1))
val stepsRecords = mutableListOf<StepsRecord>()
var sampleTime = startTime
val minutesBetweenSamples = 15L
while (sampleTime < endTime) {
    // Get the default ZoneId then convert it to an offset
    val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(sampleTime)
    stepsRecords += StepsRecord(
        startTime = sampleTime.minus(java.time.Duration.ofMinutes(minutesBetweenSamples)),
        startZoneOffset = zoneOffset,
        endTime = sampleTime,
        endZoneOffset = zoneOffset,
        count = Random.nextLong(1, 100),
        metadata = Metadata.unknownRecordingMethod(),
    )
    sampleTime = sampleTime.plus(java.time.Duration.ofMinutes(minutesBetweenSamples))
}
healthConnectClient.insertRecords(
    stepsRecords
)
如需了解详情,请参阅 ZoneId 的文档。
写入频次和细化程度
将数据写入健康数据共享时,请使用适当的分辨率。使用适当的分辨率有助于减少存储负载,同时仍能保持一致且准确的数据。数据解析包含以下两个方面:
- 写入频率:应用将新数据写入健康数据共享的频率。
- 在有新数据时尽可能频繁地写入数据,同时注意设备性能。
 - 为避免对电池续航时间和其他性能方面造成负面影响,写入操作之间的最大间隔应为 15 分钟。
 
 - 写入数据的粒度:数据的采样频率。
- 例如,每 5 秒写入一次心率样本。
 - 并非所有数据类型都需要相同的采样率。例如,每秒更新步数数据几乎没什么好处,不如降低更新频率(例如每 60 秒一次)。
 - 如果采样率较高,用户就可以更详尽、更精细地了解自己的健康与健身数据。采样频率应能够在细节和性能之间取得平衡。
 
 
更多指南
在写入数据时,请遵循以下准则:
- 每次同步时,仅写入新数据,并更新自上次同步后发生修改的数据。
 - 对请求进行分块,且每个写入请求的记录数量不超过 1,000 条。
 - 将任务限制为仅在设备空闲且电池电量充足时运行。
 - 对于后台任务,请使用 WorkManager 安排周期性任务,最长时限为 15 分钟。
 
以下代码使用 WorkManager 安排周期性后台任务,最长时限为 15 分钟,灵活间隔为 5 分钟。此配置是使用 PeriodicWorkRequest.Builder 类设置的。
val constraints = Constraints.Builder()
    .requiresBatteryNotLow()
    .requiresDeviceIdle(true)
    .build()
val writeDataWork = PeriodicWorkRequestBuilder<WriteDataToHealthConnectWorker>(
        15,
        TimeUnit.MINUTES,
        5,
        TimeUnit.MINUTES
    )
    .setConstraints(constraints)
    .build()
主动跟踪
以下建议适用于执行基于事件的跟踪(例如锻炼和睡眠)或手动用户输入(例如营养摄入)的应用。应用在前台运行或出现应用一天只被使用寥寥几次的罕见事件时,会创建此类记录。
验证应用不会在事件的整个持续期间使 Health Connect 始终保持运行状态。
必须通过以下两种方式之一将数据写入健康数据共享:
- 在活动结束后将数据同步到“健康数据共享”。例如,在用户结束一个被跟踪的锻炼时段时同步数据。
 - 使用 
WorkManager安排一次性任务,以便日后需要时同步数据。 
写入粒度和频率方面的最佳实践
将数据写入健康数据共享时,请使用适当的分辨率。使用适当的分辨率有助于减少存储负载,同时仍能保持一致且准确的数据。数据分辨率包含以下两方面:
写入频率:应用将任何新数据推送到健康数据共享的频率。在有新数据时尽可能频繁地写入数据,同时注意设备性能。为避免对电池续航时间和其他性能方面产生负面影响,写入之间的最大间隔应为 15 分钟。
写入数据的粒度:推送的数据的采样频率。例如,每 5 秒写入一次心率样本。并非所有数据类型都需要相同的采样率。例如,每秒更新步数数据几乎没什么好处,不如降低更新频率(例如每 60 秒一次)。不过,如果采样率较高,用户就可以更详尽、更精细地了解自己的健康与健身数据。采样频率应能够在细节和性能之间取得平衡。
系列数据的结构记录
对于使用一系列样本的数据类型(例如 HeartRateRecord),请务必正确构建记录。您不应创建持续更新的一整天记录,而应创建多个较小的记录,每个记录代表一个特定的时间间隔。
例如,对于心率数据,您应每分钟创建一个新的 HeartRateRecord。每条记录都将包含相应分钟的开始时间和结束时间,以及在该分钟内捕获的所有心率样本。
在与健康数据共享进行常规同步期间(例如每 15 分钟),您的应用应写入自上次同步以来创建的所有一分钟记录。这样可使记录保持在可管理的规模,并提高查询和处理数据的性能。
以下示例展示了如何创建一个时长为 1 分钟的 HeartRateRecord,其中包含多个样本:
val startTime = Instant.now().truncatedTo(ChronoUnit.MINUTES)
val endTime = startTime.plus(Duration.ofMinutes(1))
val heartRateRecord = HeartRateRecord(
    startTime = startTime,
    startZoneOffset = ZoneOffset.UTC,
    endTime = endTime,
    endZoneOffset = ZoneOffset.UTC,
    // Create a new record every minute, containing a list of samples.
    samples = listOf(
        HeartRateRecord.Sample(
            time = startTime + Duration.ofSeconds(15),
            beatsPerMinute = 80,
        ),
        HeartRateRecord.Sample(
            time = startTime + Duration.ofSeconds(30),
            beatsPerMinute = 82,
        ),
        HeartRateRecord.Sample(
            time = startTime + Duration.ofSeconds(45),
            beatsPerMinute = 85,
        )
    ),
    metadata = Metadata.autoRecorded(
        device = Device(type = Device.TYPE_WATCH)
    ))
写入全天监控的数据
对于持续收集的数据(例如步数),应用应在新数据可用时尽可能频繁地写入健康数据共享。为避免对电池续航时间和其他性能方面造成负面影响,写入之间的最大间隔应为 15 分钟。
数据类型  | 
    单位  | 
    预期  | 
    示例  | 
  
步骤  | 
    步数  | 
    每隔 1 分钟  | 
    23:14 - 23:15 - 5 步 23:16 - 23:17 - 22 步 23:17 - 23:18 - 8 步  | 
  
步频  | 
    步/分钟  | 
    每隔 1 分钟  | 
    23:14 - 23:15 - 5 步/分钟 23:16 - 23:17 - 22 步/分钟 23:17 - 23:18 - 8 步/分钟  | 
  
推轮椅次数  | 
    推送  | 
    每隔 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  | 
    米  | 
    每隔 1 分钟  | 
    20:36 - 20:37 - 3.048 米 20:39 - 20:40 - 3.048 米 23:23 - 23:24 - 9.144 米  | 
  
FloorsClimbed  | 
    楼层数  | 
    每隔 1 分钟  | 
    23:14 - 23:15 - 5 层 23:16 - 23:16 - 22 层 23:17 - 23:18 - 8 层  | 
  
HeartRate  | 
    bpm  | 
    每分钟 4 次  | 
    6:11:15 - 55 bpm 6:11:30 上午 - 56 bpm 6:11:45 - 56 bpm 6:12:00 - 55 bpm  | 
  
HeartRateVariabilityRmssd  | 
    毫秒  | 
    每隔 1 分钟  | 
    6:11am - 23 毫秒  | 
  
呼吸频率  | 
    次/分钟  | 
    每隔 1 分钟  | 
    23:14 - 23:15 - 每分钟呼吸 60 次 23:16 - 23:16 - 62 次呼吸/分钟 23:17 - 23:18 - 64 次呼吸/分钟  | 
  
OxygenSaturation  | 
    %  | 
    每隔 1 小时  | 
    6:11 - 95.208%  | 
  
应在锻炼或睡眠时段结束后将数据写入健康数据共享。对于主动跟踪(例如锻炼和睡眠)或手动用户输入(例如营养摄入),应用在前台运行或出现应用一天只被使用寥寥几次的罕见事件时,会创建此类记录。
验证应用不会在事件的整个持续期间使“健康数据共享”始终保持运行状态。
必须通过以下两种方式之一将数据写入健康数据共享:
- 在活动结束后将数据同步到“健康数据共享”。例如,在用户结束一个被跟踪的锻炼时段时同步数据。
 - 使用 WorkManager 安排一次性任务,以便日后需要时同步数据。
 
锻炼时段和睡眠时段
您的应用至少应遵循表 2 中“预期”列中的指导。请尽可能遵循最佳列中的指导。
下表显示了如何在锻炼期间写入数据:
数据类型  | 
    单位  | 
    预期  | 
    祝好  | 
    示例  | 
  
步骤  | 
    步数  | 
    每隔 1 分钟  | 
    每 1 秒  | 
    23:14-23:15 - 5 步 23:16 - 23:17 - 22 步 23:17 - 23:18 - 8 步  | 
  
步频  | 
    步/分钟  | 
    每隔 1 分钟  | 
    每 1 秒  | 
    23:14-23:15 - 35 步/分钟 23:16 - 23:17 - 37 步/分钟 23:17 - 23:18 - 40 步/分钟  | 
  
推轮椅次数  | 
    推送  | 
    每隔 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  | 
    米  | 
    每隔 1 分钟  | 
    每 1 秒  | 
    20:36 - 20:37 - 3.048 米 20:39 - 20:40 - 3.048 米 23:23 - 23:24 - 9.144 米  | 
  
ExerciseRoutes  | 
    纬度/经度/海拔高度  | 
    每 3-5 秒  | 
    每 1 秒  | 
    |
HeartRate  | 
    bpm  | 
    每分钟 4 次  | 
    每 1 秒  | 
    23:14-23:15 - 150 bpm  | 
  
表 3 显示了如何在睡眠会话期间或之后写入数据:
数据类型  | 
    单位  | 
    预期样本  | 
    示例  | 
  
睡眠分期  | 
    阶段  | 
    每个睡眠阶段的精细时间段  | 
    23:46 - 23:50 - 清醒 23:50 - 23:56 - 浅度睡眠 23:56 - 00:16 - 深睡眠  | 
  
RestingHeartRate  | 
    bpm  | 
    单个每日值(预计在早上第一时间测量)  | 
    6:11 - 60 次/分钟  | 
  
OxygenSaturation  | 
    %  | 
    单个每日值(预计在早上第一时间测量)  | 
    6:11 - 95.208%  | 
  
多运动赛事
此方法使用现有的数据类型和结构,并验证与当前健康数据共享实现和数据读取器的兼容性。这是健身平台常用的方法。
此外,游泳、骑自行车和跑步等单个锻炼项目在“健康数据共享”中并非固有地关联在一起,数据读取者必须根据这些锻炼项目的时间邻近性来推断它们之间的关系。各分段之间的过渡(例如从游泳到骑车)不会明确显示。
以下示例展示了如何写入铁人三项的数据:
val swimStartTime = Instant.parse("2024-08-22T08:00:00Z")
val swimEndTime = Instant.parse("2024-08-22T08:30:00Z")
val bikeStartTime = Instant.parse("2024-08-22T08:40:00Z")
val bikeEndTime = Instant.parse("2024-08-22T09:40:00Z")
val runStartTime = Instant.parse("2024-08-22T09:50:00Z")
val runEndTime = Instant.parse("2024-08-22T10:20:00Z")
val swimSession = ExerciseSessionRecord(
    startTime = swimStartTime,
    endTime = swimEndTime,
    exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER,
    metadata = Metadata.autoRecorded(
      device = Device(type = Device.TYPE_WATCH)
    )
)
val bikeSession = ExerciseSessionRecord(
    startTime = bikeStartTime,
    endTime = bikeEndTime,
    exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
    metadata = Metadata.autoRecorded(
      device = Device(type = Device.TYPE_WATCH)
    )
)
val runSession = ExerciseSessionRecord(
    startTime = runStartTime,
    endTime = runEndTime,
    exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
    metadata = Metadata.autoRecorded(
      device = Device(type = Device.TYPE_WATCH)
    )
)
healthConnectClient.insertRecords(listOf(swimSession, bikeSession, runSession))
处理异常
遇到问题时,Health Connect 会针对 CRUD 操作抛出标准异常。您的应用应酌情捕获和处理每个异常。
HealthConnectClient 中的每种方法都会列出可能会被抛出的异常。一般而言,您的应用应处理以下异常:
| 异常 | 说明 | 建议的最佳实践 | 
|---|---|---|
IllegalStateException
 | 您可能遇到了以下情况之一:
  | 在发出请求之前,先处理可能存在的输入问题。最好能为变量赋值或在自定义函数中将它们用作参数,而不是直接在请求中使用这些值,以便顺利应用错误处理策略。 | 
IOException
 | 从磁盘中读取和向其中写入数据时遇到问题。 | 为避免此类问题,请参考以下建议:
  | 
RemoteException
 | SDK 所关联的底层服务内部或与该服务通信时出现错误。 例如,您的应用尝试删除具有给定 uid 的记录。不过,当应用在检查底层服务后发现记录不存在时,会抛出异常。
 | 为避免此类问题,请参考以下建议:
  | 
SecurityException
 | 相应请求需要用到未被授予的权限时遇到问题。 | 为避免此类问题,请确保您已为已发布的应用声明健康数据共享数据类型的使用情况。另外,您必须在清单文件和您的 activity 中声明健康数据共享权限。 |