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