사용자 활력 징후를 관리하는 앱을 빌드하려는 경우 헬스 커넥트를 사용하여 다음과 같은 작업을 할 수 있습니다.
- 다른 앱에서 혈압, 심박수, 체온과 같은 활력 징후 데이터 읽기
- 앱 또는 연결된 기기에서 기록한 활력 징후 데이터 쓰기
- 활력 징후 데이터를 기반으로 추세를 모니터링하고 건강 통계 제공
이 가이드에서는 권한, 읽기 및 쓰기 워크플로, 권장사항을 다루면서 활력 징후 데이터 유형을 사용하는 방법을 설명합니다.
개요: 포괄적인 활력 징후 추적기 빌드
다음과 같은 핵심 단계를 따라 헬스 커넥트를 사용하여 포괄적인 활력 징후 추적 환경을 빌드할 수 있습니다.
- 활력 징후 데이터 유형에 적절한 권한을 요청합니다.
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)) }
일괄 쓰기
앱에서 연결된 기기 또는 백엔드 서비스의 데이터를 동기화하는 것과 같이 여러 데이터 포인트를 써야 하는 경우 효율성을 개선하고 배터리 소모를 줄이기 위해 쓰기를 일괄 처리해야 합니다. 헬스 커넥트는 단일 쓰기 요청에서 최대 1,000개의 기록을 처리할 수 있습니다.
다음 코드는 여러 기록을 한 번에 일괄 쓰기하는 방법을 보여줍니다.
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를 사용합니다. 이렇게 하면 특정 시점 이후에 삽입, 업데이트 또는 삭제된 기록 목록을 검색할 수 있으므로 변경사항을 수동으로 추적하거나 모든 데이터를 반복적으로 읽는 것보다 효율적입니다. 자세한 내용은 헬스 커넥트로 데이터 동기화를 참고하세요.
권장사항
데이터 안정성과 사용자 환경을 개선하려면 다음 가이드라인을 따르세요.
- 쓰기 빈도 및 일괄 처리: 입력/출력 오버헤드를 줄이고
배터리 수명을 보존하려면 각 포인트를 개별적으로 쓰는 대신 최대 1,000개의 기록이 있는 일괄 처리로 데이터 포인트를 단일
insertRecords호출로 그룹화합니다.- 실시간 추적: 연속 혈당 측정기 또는 심박계와 같은 센서에서 자주 업데이트하려면 실시간 업데이트와 배터리 효율성의 균형을 맞추기 위해 최대 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 예외와 같은 극단적인 사례를 앱에서 처리하는 방법을 확인합니다.
품질 체크리스트
일반적인 아키텍처
활력 징후 구현에는 일반적으로 다음이 포함됩니다.
| 구성요소 | 관리 |
|---|---|
| 활력 징후 컨트롤러 | 센서/입력 읽기 일괄 처리 로직 |
| 저장소 레이어 (헬스 커넥트 작업 래핑): | 활력 징후 기록 삽입 활력 징후 기록 읽기 |
| UI 레이어 (표시): | 실시간 수치 이전 데이터 차트 및 추세 |
문제 해결
| 증상 | 가능한 원인 | 해결 방법 |
|---|---|---|
| 데이터 유형 누락 (예: 혈압) | 쓰기 권한 누락 또는 잘못된 시간 필터 | 특정 데이터 유형 권한을 요청했고 사용자가 부여했는지 확인합니다. ReadRecordsRequest가 측정 시간을 포함하는 TimeRangeFilter를 사용하는지 확인합니다. 권한을 참고하세요. |
| 중복 기록이 표시됨 | clientRecordId 누락 |
각 기록의 Metadata에 고유한 clientRecordId를 할당합니다. 이렇게 하면 동기화 재시도 중에 동일한 데이터가 두 번 작성되는 경우 헬스 커넥트에서 중복 삭제를 실행할 수 있습니다. 권장사항을 참고하세요. |
| 기록을 쓸 수 없음 | 유효한 범위를 벗어난 잘못된 단위 또는 값 | 헬스 커넥트는 기록 값을 확인합니다. 예를 들어 혈압 값은 생리적으로 가능한 범위에 있어야 합니다. 유효한 범위와 단위는 데이터 유형 문서를 참고하세요. |
일반적인 디버깅 단계
| 권한 상태를 확인합니다. | 읽기 또는 쓰기 작업을 시도하기 전에 항상 getPermissionStatus()를 호출합니다. 사용자는 언제든지 시스템 설정에서 권한을 취소할 수 있습니다. |