개인 건강 기록 (PHR)에 Android 프레임워크 API를 사용하려면 다음을 실행해야 합니다.
- Android 스튜디오를 사용하여 환경 구성
HealthConnectManager
인스턴스화- 권한 처리
- 데이터 소스 만들기
Android 스튜디오를 사용하여 환경 구성
최신 API에 액세스하려면 최신 관련 도구에도 액세스해야 합니다.
- Android 스튜디오의 최신 버전을 설치합니다.
- Android 스튜디오에서 Tools > AVD Manager로 이동합니다.
- SDK Platforms 탭에서 Android Baklava를 선택합니다.
- 이 버전에는 API 수준 36 및 PHR API가 포함되어 있습니다.
- SDK Tools 탭에서 사용 가능한 최신 Android SDK Build-Tools를 선택합니다.
- '확인'을 클릭하여 SDK를 설치합니다.
- 사용 가능한 최신 버전의 Android Gradle 플러그인을 사용하세요.
API 수준 선언
PHR API에 액세스하려면 적절한 버전의 Android를 타겟팅해야 합니다. app/build.gradle
에서 API 수준을 선언하면 됩니다.
...
compileSdk = 36
defaultConfig {
targetSdk = 36
...
uses-sdk-element 및 Android 16 SDK 설정 문서에서 API 수준 구성에 관해 자세히 알아보세요.
권한 선언
AndroidManifest.xml
에 새로운 PHR 권한을 추가합니다.
애플리케이션에서 사용할 데이터 유형에 대해서만 권한을 선언합니다. 의료 관련 권한의 전체 목록은 코드 블록에 표시됩니다. 헬스 커넥트의 PHR 이외 권한은 포함되지 않습니다.
...
<!-- Medical permissions -->
<uses-permission android:name="android.permission.health.WRITE_MEDICAL_DATA"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_ALLERGIES_INTOLERANCES"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_CONDITIONS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_IMMUNIZATIONS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_LABORATORY_RESULTS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_MEDICATIONS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PERSONAL_DETAILS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PRACTITIONER_DETAILS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PREGNANCY"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PROCEDURES"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_SOCIAL_HISTORY"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_VISITS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_VITAL_SIGNS"/>
...
애플리케이션이 권한이 필요한 API를 실행하려고 하는데 이 권한이 앱의 매니페스트에 선언되지 않은 경우 SecurityException
이 발생합니다.
SecurityException
유형의 예외의 경우 exception.localizedMessage
에서 유용한 오류 메시지를 사용할 수 있습니다.
HealthConnectManager 인스턴스화
HealthConnectManager
는 권한 상호작용과 헬스 커넥트 로컬 데이터 저장소에 대한 읽기 및 쓰기를 처리하는 클래스입니다. 다음 섹션에서는 HealthConnectManager
의 인스턴스 메서드를 살펴봅니다. HealthConnectManager는 시스템 서비스를 노출하므로 이 클래스를 직접 인스턴스화할 수 없으며 getSystemService
를 사용해야 합니다.
특히 시스템 서비스는 인스턴스화되는 컨텍스트와 밀접하게 결합되며 해당 컨텍스트 외부에서 액세스할 수 없어야 합니다.
import android.health.connect.HealthConnectManager
...
val healthConnectManager: HealthConnectManager = requireNotNull(applicationContext.getSystemService(HealthConnectManager::class.java))
권한 처리
애플리케이션의 사용자는 애플리케이션에 헬스 커넥트 데이터에 액세스할 권한을 부여해야 합니다. 이렇게 하려면 권한이 지정된 새 활동을 실행하고 부여된 권한 목록을 사용합니다. 새 활동의 UI를 통해 사용자는 앱에 부여해야 하는 권한을 선택할 수 있습니다. 애플리케이션에서 사용할 데이터 유형에 대해서만 권한을 요청하세요.
val MEDICAL_PERMISSIONS = arrayOf(
"android.permission.health.READ_MEDICAL_DATA_ALLERGIES_INTOLERANCES",
"android.permission.health.READ_MEDICAL_DATA_CONDITIONS",
"android.permission.health.READ_MEDICAL_DATA_IMMUNIZATIONS",
"android.permission.health.READ_MEDICAL_DATA_LABORATORY_RESULTS",
"android.permission.health.READ_MEDICAL_DATA_MEDICATIONS",
"android.permission.health.READ_MEDICAL_DATA_PERSONAL_DETAILS",
"android.permission.health.READ_MEDICAL_DATA_PRACTITIONER_DETAILS",
"android.permission.health.READ_MEDICAL_DATA_PREGNANCY",
"android.permission.health.READ_MEDICAL_DATA_PROCEDURES",
"android.permission.health.READ_MEDICAL_DATA_SOCIAL_HISTORY",
"android.permission.health.READ_MEDICAL_DATA_VISITS",
"android.permission.health.READ_MEDICAL_DATA_VITAL_SIGNS",
"android.permission.health.WRITE_MEDICAL_DATA",
)
...
private lateinit var mRequestPermissionLauncher: ActivityResultLauncher<Array<String>>
...
mRequestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
permissionMap: Map<String, Boolean> ->
requestPermissionResultHandler(permissionMap)
}
}
...
private fun requestPermissionResultHandler(permissionMap: Map<String, Boolean>) {
// Evaluate permissionMap and handle any missing permissions
}
...
mRequestPermissionLauncher.launch(MEDICAL_PERMISSIONS)
필요한 권한 없이 읽거나 쓰려고 하면 HealthConnectException
이 발생합니다.
HealthConnectException
유형의 예외의 경우 exception.localizedMessage
에서 유용한 오류 메시지를 사용할 수 있습니다.
데이터 소스 만들기
헬스 커넥트에 건강 데이터를 쓰려면 먼저 애플리케이션에서 정보를 보관할 데이터 소스를 만들어야 합니다. 데이터 소스는 일반적으로 특정 API 또는 의료 시스템을 나타냅니다.
이 예에서는 My Hospital
라는 데이터 소스를 만들고 FHIR 버전을 지정합니다.
import android.health.connect.CreateMedicalDataSourceRequest
import android.health.connect.HealthConnectManager
import android.health.connect.datatypes.FhirVersion
import android.health.connect.datatypes.MedicalDataSource
import android.net.Uri
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.os.asOutcomeReceiver
import kotlinx.coroutines.suspendCancellableCoroutine
...
private suspend fun createMedicalDataSource(
fhirBaseUri: Uri,
displayName: String,
fhirVersion: FhirVersion,
): String {
val dataSource =
suspendCancellableCoroutine<MedicalDataSource> { continuation ->
healthConnectManager.createMedicalDataSource(
CreateMedicalDataSourceRequest.Builder(fhirBaseUri,
displayName,
fhirVersion).build(),
Runnable::run,
continuation.asOutcomeReceiver(),
)
}
Log.d("CREATE_DATA_SOURCE", "Created source: $dataSource")
return "Created data source: $displayName"
}
...
createMedicalDataSource(
Uri.parse("example.fhir.com/R4/123"),
"My Hospital",
FhirVersion.parseFhirVersion("4.0.1"),
)
레코드 작성
JSON에서 FHIR 레코드 예시를 준비합니다. 웹에는 FHIR 형식의 예시 데이터가 있는 다양한 소스가 있습니다.
{
"resourceType": "Immunization",
"id": "immunization-1",
"status": "completed",
"vaccineCode": {
"coding": [
{
"system": "http://hl7.org/fhir/sid/cvx",
"code": "115"
},
{
"system": "http://hl7.org/fhir/sid/ndc",
"code": "58160-842-11"
}
],
"text": "Tdap"
},
"patient": {
"reference": "Patient/patient_1",
"display": "Example, Anne"
},
"encounter": {
"reference": "Encounter/encounter_unk",
"display": "GP Visit"
},
"occurrenceDateTime": "2018-05-21",
"primarySource": true,
"manufacturer": {
"display": "Sanofi Pasteur"
},
"lotNumber": "1",
"site": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ActSite",
"code": "LA",
"display": "Left Arm"
}
],
"text": "Left Arm"
},
"route": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-RouteOfAdministration",
"code": "IM",
"display": "Injection, intramuscular"
}
],
"text": "Injection, intramuscular"
},
"doseQuantity": {
"value": 0.5,
"unit": "mL"
},
"performer": [
{
"function": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0443",
"code": "AP",
"display": "Administering Provider"
}
],
"text": "Administering Provider"
},
"actor": {
"reference": "Practitioner/practitioner_1",
"type": "Practitioner",
"display": "Dr Maria Hernandez"
}
}
]
}
데이터를 삽입합니다.
import android.health.connect.UpsertMedicalResourceRequest
import android.health.connect.datatypes.MedicalResource
...
private suspend fun loadJSONFromAsset(assetName: String): String {
...
private suspend fun upsertMedicalResources(
requests: List<UpsertMedicalResourceRequest>
): List<MedicalResource> {
Log.d("UPSERT_RESOURCES", "Writing ${requests.size} resources")
val resources =
suspendCancellableCoroutine<List<MedicalResource>> { continuation ->
healthConnectManager.upsertMedicalResources(
requests,
Runnable::run,
continuation.asOutcomeReceiver(),
)
}
Log.d("UPSERT_RESOURCES", "Wrote ${resources.size} resources")
return resources
}
...
private suspend fun insertResource(insertedDataSourceId: String, resource: String): String {
val insertedResources =
upsertMedicalResources(
listOf(
UpsertMedicalResourceRequest.Builder(
insertedDataSourceId,
FhirVersion.parseFhirVersion("4.0.1"),
resource,
)
.build()
)
)
return insertedResources.joinToString(
separator = "\n",
transform = MedicalResource::toString,
)
}
...
val immunizationResource =
loadJSONFromAsset("immunization_1.json")
insertResource(dataSource.id, immunizationResource)
upsertMedicalResources
는 UpsertMedicalResourceRequest
목록을 인수로 사용합니다. 호출에 전달된 개별 UpsertMedicalResourceRequest
가 삽입되지 않으면 전체 UpsertMedicalResourceRequest
목록에 대한 쓰기가 저장소에 커밋되지 않습니다.
요청에 잘못된 MedicalDataSource
ID가 포함된 경우 API에서 IllegalArgumentException
을 발생시킵니다. 다른 이유로 요청이 유효하지 않은 것으로 간주되면 호출자에게 HealthConnectException
이 수신됩니다.
특정 요청의 고유 키는 데이터 소스 ID, FHIR 리소스 유형, FHIR 리소스 ID의 조합입니다. 요청의 세 항목이 기존 레코드와 일치하면 업데이트가 트리거됩니다. 그렇지 않으면 새 레코드가 생성됩니다.
레코드 읽기
유형별로 레코드를 읽은 다음 적절하다고 생각하는 대로 결과를 처리합니다.
import android.health.connect.ReadMedicalResourcesInitialRequest
import android.health.connect.ReadMedicalResourcesResponse
import android.health.connect.datatypes.MedicalResource.MEDICAL_RESOURCE_TYPE_IMMUNIZATIONS
...
private suspend fun readImmunization(): List<MedicalResource> {
var receiver: OutcomeReceiver<ReadMedicalResourcesResponse, HealthConnectException>
val request: ReadMedicalResourcesInitialRequest =
ReadMedicalResourcesInitialRequest.Builder(MEDICAL_RESOURCE_TYPE_IMMUNIZATIONS).build()
val resources =
suspendCancellableCoroutine<ReadMedicalResourcesResponse> { continuation ->
receiver = continuation.asOutcomeReceiver()
healthConnectManager.readMedicalResources(request, Runnable::run, receiver)
}
.medicalResources
Log.d("READ_MEDICAL_RESOURCES", "Read ${resources.size} resources")
return resources
}