如果您想构建一款管理用户生命体征的应用,可以使用健康数据共享来执行以下操作:
- 从其他应用读取血压、心率和体温等生命体征数据
- 写入应用或关联设备记录的生命体征数据
- 根据生命体征数据监控趋势并提供健康数据分析
本指南介绍了如何使用生命体征数据类型,涵盖了权限、读取和写入工作流以及最佳实践。
概览:构建全面的 Vitals 跟踪器
您可以使用健康数据共享功能打造全面的生命体征跟踪体验,只需按以下核心步骤操作:
- 为生命体征数据类型请求适当的权限。
- 使用
BloodPressureRecord、HeartRateRecord等记录写入生命体征数据。 - 读取生命体征数据以进行显示、分析或同步。
- 使用批处理来高效地写入和读取数据。
此工作流可实现与其他健康数据共享应用的互操作性,并验证用户控制的数据访问权限。
准备工作
在实现核心指标功能之前:
- 使用适当的依赖项集成健康数据共享。
- 创建
HealthConnectClient实例。 - 验证您的应用是否基于健康数据权限实现运行时权限流程。
主要概念
健康数据共享中的生命体征数据由各种记录类型表示,每种记录类型对应于特定的生理测量数据。与锻炼会话不同,生命体征通常以时间点数据或基于时间间隔的数据形式记录。
生命体征数据类型
生命体征数据由各个记录类型表示。常见类型包括:
BloodPressureRecord:表示单次血压读数,包括收缩压和舒张压,以及身体姿势。HeartRateRecord:表示一系列心率测量值。RestingHeartRateRecord:表示单次静息心率测量结果。BodyTemperatureRecord:表示单次体温读数,包括测量位置。BloodGlucoseRecord:表示单次血糖读数,包括与餐食和样本来源的关系。OxygenSaturationRecord:表示单次血氧饱和度读数。RespiratoryRateRecord:表示单次呼吸频率测量结果。
如需查看数据类型的完整列表,请参阅 健康数据共享数据类型。
开发注意事项
生命体征数据可能比较敏感,应用可能需要写入数据以响应来自传感器或用户输入的测量结果,或者从后端同步数据。权限对于处理生命体征数据至关重要。
权限
您的应用必须先请求相关的健康数据共享权限,然后才能读取或写入生命体征数据。生命体征的常见权限包括血压、心率、体温、血糖、血氧饱和度和呼吸频率。您可以执行以下操作:
- 血压:对
BloodPressureRecord的读取和写入权限。 - 心率:针对
HeartRateRecord的读取和写入权限。 - 静息心率:对
RestingHeartRateRecord的读取和写入权限。 - 体温:对
BodyTemperatureRecord的读取和写入权限。 - 血糖:针对
BloodGlucoseRecord的读取和写入权限。 - 血氧饱和度:针对
OxygenSaturationRecord的读取和写入权限。 - 呼吸频率:对
RespiratoryRateRecord的读取和写入权限。
以下示例展示了如何请求血压、心率和体温权限:
创建客户端实例后,应用需要向用户请求权限。用户必须能够随时授予或拒绝权限。
为此,请为所需的数据类型创建一组权限。 先确保此集中的权限已在 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_PRESSUREandroid.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等区间数据,请验证startTime和endTime是否正确。
测试
如需验证数据正确性并确保提供优质的用户体验,请遵循以下测试策略,并参阅官方测试热门使用情形文档。
验证工具
- 健康数据共享工具箱:使用此配套应用可手动检查记录、删除测试数据和模拟数据库更改。这是验证记录是否正确存储的最佳方式。
- 使用
FakeHealthConnectClient进行单元测试:使用测试库验证应用如何处理边缘情况,例如权限撤消或 API 异常,而无需使用实体设备。
质量核对清单
典型架构
Android Vitals 实现通常包括:
| 组件 | 管理 |
|---|---|
| 生命体征控制器 | 传感器/输入读数 批处理逻辑 |
| 代码库层(封装了健康数据共享操作): | 插入生命体征记录 读取生命体征记录 |
| 界面层(显示): | 实时读数 历史数据 图表和趋势 |
问题排查
| 问题 | 可能的原因 | 分辨率 |
|---|---|---|
| 缺少数据类型(例如,血压) | 缺少写入权限或时间过滤条件不正确。 | 检查您是否已请求并获得用户授予的特定数据类型权限。验证您的 ReadRecordsRequest 是否使用了涵盖衡量时间的 TimeRangeFilter。请参阅权限。 |
| 记录写入失败 | 单位不正确或值超出有效范围。 | 健康数据共享会验证记录值。例如,血压值必须在生理学上合理的范围内。请查看数据类型文档,了解有效范围和单位。 |
| 显示重复记录 | 缺少 clientRecordId |
在每条记录的 Metadata 中分配唯一的 clientRecordId。这样一来,如果同步重试期间写入了两次相同的数据,健康数据共享便可以执行去重操作。请参阅最佳实践。 |
常见调试步骤
- 检查权限状态:在尝试执行读取或写入操作之前,请务必先调用
getPermissionStatus()。用户可以随时在系统设置中撤消权限。