利用“健康数据共享”开发生命体征体验

如果您想构建一款管理用户生命体征的应用,可以使用健康数据共享来执行以下操作:

  • 从其他应用读取血压、心率和体温等生命体征数据
  • 写入应用或关联设备记录的生命体征数据
  • 根据生命体征数据监控趋势并提供健康数据分析

本指南介绍了如何使用生命体征数据类型,涵盖了权限、读取和写入工作流以及最佳实践。

概览:构建全面的 Vitals 跟踪器

您可以使用健康数据共享功能打造全面的生命体征跟踪体验,只需按以下核心步骤操作:

  • 为生命体征数据类型请求适当的权限。
  • 使用 BloodPressureRecordHeartRateRecord 等记录写入生命体征数据。
  • 读取生命体征数据以进行显示、分析或同步。
  • 使用批处理来高效地写入和读取数据。

此工作流可实现与其他健康数据共享应用的互操作性,并验证用户控制的数据访问权限。

准备工作

在实现核心指标功能之前:

主要概念

健康数据共享中的生命体征数据由各种记录类型表示,每种记录类型对应于特定的生理测量数据。与锻炼会话不同,生命体征通常以时间点数据或基于时间间隔的数据形式记录。

生命体征数据类型

生命体征数据由各个记录类型表示。常见类型包括:

如需查看数据类型的完整列表,请参阅 健康数据共享数据类型

开发注意事项

生命体征数据可能比较敏感,应用可能需要写入数据以响应来自传感器或用户输入的测量结果,或者从后端同步数据。权限对于处理生命体征数据至关重要。

权限

您的应用必须先请求相关的健康数据共享权限,然后才能读取或写入生命体征数据。生命体征的常见权限包括血压、心率、体温、血糖、血氧饱和度和呼吸频率。您可以执行以下操作:

以下示例展示了如何请求血压、心率和体温权限:

创建客户端实例后,应用需要向用户请求权限。用户必须能够随时授予或拒绝权限。

为此,请为所需的数据类型创建一组权限。 先确保此集中的权限已在 Android 清单中声明。

// Create a set of permissions for required data types
val PERMISSIONS =
    setOf(
  HealthPermission.getReadPermission(BloodPressureRecord::class),
  HealthPermission.getWritePermission(BloodPressureRecord::class),
  HealthPermission.getReadPermission(HeartRateRecord::class),
  HealthPermission.getWritePermission(HeartRateRecord::class),
  HealthPermission.getReadPermission(BodyTemperatureRecord::class),
  HealthPermission.getWritePermission(BodyTemperatureRecord::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)
  }
}

由于用户可以随时授予或撤消权限,因此您的应用需要在每次使用权限之前检查权限,并处理权限丢失的情况。

如需请求权限,请调用 checkPermissionsAndRun 函数:

if (!granted.containsAll(PERMISSIONS)) {
    requestPermissions.launch(PERMISSIONS)
    // Check if required permissions are not granted, and return
  }
// Permissions already granted; proceed with inserting or reading data

如果您只需要为单个数据类型(例如血压)请求权限,请仅在权限集中包含该数据类型:

对血压的访问权限受以下权限保护:

  • android.permission.health.READ_BLOOD_PRESSURE
  • android.permission.health.WRITE_BLOOD_PRESSURE

如要向应用添加血压功能,请先请求 BloodPressureRecord 数据类型的权限。

您需要声明以下权限才能写入血压:

<application>
  <uses-permission
android:name="android.permission.health.WRITE_BLOOD_PRESSURE" />
...
</application>

如需读取血压,您需要请求以下权限:

<application>
  <uses-permission
android:name="android.permission.health.READ_BLOOD_PRESSURE" />
...
</application>

写入生命体征数据

本部分介绍了如何将生命体征数据写入健康数据共享。生命体征数据通常以单个记录的形式写入。如果您要一次写入多条记录(例如从传感器或后端同步),请使用批处理。

撰写 BloodPressureRecord 的示例:

suspend fun writeBloodPressureRecord(healthConnectClient: HealthConnectClient) {
    val record = BloodPressureRecord(
        time = Instant.now(),
        zoneOffset = ZoneOffset.UTC,
        systolic = Pressure.millimetersOfMercury(120.0),
        diastolic = Pressure.millimetersOfMercury(80.0),
        bodyPosition = BloodPressureRecord.BODY_POSITION_SITTING_DOWN,
        measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST
    )
    healthConnectClient.insertRecords(listOf(record))
}

批量写入

如果您的应用需要写入多个数据点(例如同步来自已连接的设备或后端服务的数据),您应批量写入,以提高效率并减少电池消耗。健康数据共享可以在单个写入请求中处理最多 1000 条记录。

以下代码展示了如何一次批量写入多条记录:

suspend fun writeBatchRecords(healthConnectClient: HealthConnectClient) {
    val bloodPressureRecord = BloodPressureRecord(
        time = Instant.now(),
        zoneOffset = ZoneOffset.UTC,
        systolic = Pressure.millimetersOfMercury(120.0),
        diastolic = Pressure.millimetersOfMercury(80.0),
        bodyPosition = BloodPressureRecord.BODY_POSITION_SITTING_DOWN,
        measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST
    )
    val heartRateRecord = HeartRateRecord(
        startTime = Instant.now().minusSeconds(60),
        startZoneOffset = ZoneOffset.UTC,
        endTime = Instant.now(),
        endZoneOffset = ZoneOffset.UTC,
        samples = listOf(HeartRateRecord.Sample(time = Instant.now().minusSeconds(30), beatsPerMinute = 80))
    )
    healthConnectClient.insertRecords(listOf(bloodPressureRecord, heartRateRecord))
}

读取生命体征数据

应用可以读取生命体征数据,以显示测量结果、分析趋势或将数据与外部服务器同步。如需读取生命体征,请使用 ReadRecordsRequest 并指定记录类型,然后按时间范围进行过滤。

读取 BloodPressureRecord 数据的示例:

suspend fun readBloodPressureRecords(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    val response = healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = BloodPressureRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )

    for (record in response.records) {
        // Process each blood pressure record
        val systolic = record.systolic
        val diastolic = record.diastolic
    }
}

如果您需要将生命体征数据与后端服务器同步,或者让应用的数据存储区与健康数据共享保持同步,请使用 ChangeLogs。这样一来,您就可以检索自特定时间点以来插入、更新或删除的记录列表,这比手动跟踪更改或重复读取所有数据更高效。如需了解详情,请参阅通过“健康数据共享”同步数据

最佳做法

请遵循以下准则,以提高数据可靠性和用户体验:

  • 写入频率和批处理:为了减少输入/输出开销并延长电池续航时间,请将数据点分组到单个 insertRecords 调用中,每批最多包含 1000 条记录,而不是单独写入每个点。
    • 实时跟踪:对于来自传感器(例如持续血糖监测器或心率监测器)的频繁更新,请以批处理方式写入数据,间隔时间最长为 15 分钟,以平衡实时更新与电池效率。
    • 后台同步:使用 WorkManager 进行延迟写入,例如同步来自配套设备或后端服务的数据。争取将批量写入的时间间隔设为 15 分钟。
  • 避免写入重复数据:使用客户端 ID 创建记录时,请设置 metadata.clientRecordId。健康数据共享使用此属性来标识唯一记录。如果您尝试写入具有已存在的 clientRecordId 的记录,健康数据共享会忽略重复记录或更新现有记录,而不是创建新记录。设置 metadata.clientRecordId 是防止在同步重试或应用重新安装期间出现重复项的最有效方法。

    val record = BloodPressureRecord(
        time = Instant.now(),
        zoneOffset = ZoneOffset.UTC,
        systolic = Pressure.millimetersOfMercury(120.0),
        diastolic = Pressure.millimetersOfMercury(80.0),
        bodyPosition = BloodPressureRecord.BODY_POSITION_SITTING_DOWN,
        measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
        metadata = Metadata(
            // Use a unique ID from your own database
            clientRecordId = "bp_20240101_user123"
        )
    )
  • 检查现有数据:在同步数据之前,请查询“健康数据共享”中同步时间范围内的记录,看看您的应用中是否已存在数据,以避免重复或覆盖较新的数据。

  • 提供清晰的权限使用理由:使用 Permission.createIntent 流程说明您的应用为何需要访问健康数据,例如“为了监控您的血压趋势并提供数据分析洞见”。

  • 使时间戳与测量结果保持一致:验证记录时间戳是否准确反映了测量时间。对于 HeartRateRecord 等区间数据,请验证 startTimeendTime 是否正确。

测试

如需验证数据正确性并确保提供优质的用户体验,请遵循以下测试策略,并参阅官方测试热门使用情形文档。

验证工具

质量核对清单

典型架构

Android Vitals 实现通常包括:

组件 管理
生命体征控制器 传感器/输入读数
批处理逻辑
代码库层(封装了健康数据共享操作): 插入生命体征记录
读取生命体征记录
界面层(显示): 实时读数
历史数据
图表和趋势

问题排查

问题 可能的原因 分辨率
缺少数据类型(例如,血压) 缺少写入权限或时间过滤条件不正确。 检查您是否已请求并获得用户授予的特定数据类型权限。验证您的 ReadRecordsRequest 是否使用了涵盖衡量时间的 TimeRangeFilter。请参阅权限
记录写入失败 单位不正确或值超出有效范围。 健康数据共享会验证记录值。例如,血压值必须在生理学上合理的范围内。请查看数据类型文档,了解有效范围和单位。
显示重复记录 缺少 clientRecordId 在每条记录的 Metadata 中分配唯一的 clientRecordId。这样一来,如果同步重试期间写入了两次相同的数据,健康数据共享便可以执行去重操作。请参阅最佳实践

常见调试步骤

  • 检查权限状态:在尝试执行读取或写入操作之前,请务必先调用 getPermissionStatus()。用户可以随时在系统设置中撤消权限。