Чтобы использовать API платформы Android для личных медицинских записей (PHR), необходимо:
- Настройте среду с помощью Android Studio
- Создание экземпляра
HealthConnectManager
- Обработка разрешений
- Создание источников данных
Настройте среду с помощью Android Studio
Чтобы иметь доступ к новейшим API, вам также необходим доступ к новейшим соответствующим инструментам:
- Установите последнюю версию Android Studio .
- В Android Studio выберите «Инструменты» > «Менеджер SDK» .
- На вкладке «Платформы SDK» выберите Android Baklava .
- Эта версия включает API уровня 36 и API PHR.
- На вкладке «Инструменты SDK» выберите последнюю доступную версию Android SDK Build-Tools .
- Нажмите «ОК», чтобы установить SDK.
- Используйте последнюю доступную версию плагина Android Gradle .
Объявить уровни API
Чтобы получить доступ к API PHR, вам необходимо выбрать соответствующую версию Android. Это делается путем объявления уровней API в app/build.gradle
.
...
compileSdk = 36
defaultConfig {
targetSdk = 36
...
Узнайте больше о настройке уровней API в элементе Uses-sdk-element и о настройке документации Android 16 SDK .
Объявить разрешения
Добавьте новые разрешения PHR в AndroidManifest.xml
. Объявляйте разрешения только для тех типов данных, которые вы собираетесь использовать в своем приложении. Полный список медицинских разрешений показан в блоке кода. Разрешения, не связанные с PHR, для Health Connect не включены.
...
<!-- 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
— это класс, отвечающий за обработку взаимодействий с разрешениями, а также за чтение и запись в локальный репозиторий данных Health Connect. Мы рассмотрим методы экземпляра HealthConnectManager
в последующих разделах. Поскольку HealthConnectManager предоставляет системную службу, вы не можете напрямую создать экземпляр этого класса и должны использовать getSystemService
. В частности, обратите внимание, что системная служба тесно связана с контекстом, в котором она создана, и не должна быть доступна вне этого контекста.
import android.health.connect.HealthConnectManager
...
val healthConnectManager: HealthConnectManager = requireNotNull(applicationContext.getSystemService(HealthConnectManager::class.java))
Обработка разрешений
Пользователь вашего приложения должен предоставить вашему приложению разрешение на доступ к данным Health Connect. Для этого запустите новое действие с указанными разрешениями и используйте полученный список предоставленных разрешений. Пользовательский интерфейс нового действия позволит пользователю выбирать разрешения, которые ему необходимо предоставить вашему приложению. Запрашивайте разрешения только для тех типов данных, которые вы планируете использовать в своем приложении.
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
.
Создание источников данных
Чтобы записать данные о состоянии здоровья в Health Connect, ваше приложение должно сначала создать источник данных для хранения информации. Источник данных обычно представляет собой конкретный 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"),
)
Запись записей
Подготовьте примеры записей FHIR в формате JSON. В сети существуют различные источники с примерами данных в формате 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
, API выдаст исключение IllegalArgumentException
. Если какой-либо запрос будет признан недействительным по каким-либо другим причинам, вызывающая сторона получит HealthConnectException
.
Уникальный ключ для конкретного запроса — это комбинация идентификатора источника данных, типа ресурса FHIR и идентификатора ресурса FHIR. Если эти три элемента в запросе соответствуют существующей записи, запускается обновление. В противном случае создается новая запись.
Чтение записей
Прочитайте записи по типу, а затем обработайте результаты по своему усмотрению.
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
}
Чтобы использовать API платформы Android для личных медицинских записей (PHR), необходимо:
- Настройте среду с помощью Android Studio
- Создание экземпляра
HealthConnectManager
- Обработка разрешений
- Создание источников данных
Настройте среду с помощью Android Studio
Чтобы иметь доступ к новейшим API, вам также необходим доступ к новейшим соответствующим инструментам:
- Установите последнюю версию Android Studio .
- В Android Studio выберите «Инструменты» > «Менеджер SDK» .
- На вкладке «Платформы SDK» выберите Android Baklava .
- Эта версия включает API уровня 36 и API PHR.
- На вкладке «Инструменты SDK» выберите последнюю доступную версию Android SDK Build-Tools .
- Нажмите «ОК», чтобы установить SDK.
- Используйте последнюю доступную версию плагина Android Gradle .
Объявить уровни API
Чтобы получить доступ к API PHR, вам необходимо выбрать соответствующую версию Android. Это делается путем объявления уровней API в app/build.gradle
.
...
compileSdk = 36
defaultConfig {
targetSdk = 36
...
Узнайте больше о настройке уровней API в элементе Uses-sdk-element и о настройке документации Android 16 SDK .
Объявить разрешения
Добавьте новые разрешения PHR в AndroidManifest.xml
. Объявляйте разрешения только для тех типов данных, которые вы собираетесь использовать в своем приложении. Полный список медицинских разрешений показан в блоке кода. Разрешения, не связанные с PHR, для Health Connect не включены.
...
<!-- 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
— это класс, отвечающий за обработку взаимодействий с разрешениями, а также за чтение и запись в локальный репозиторий данных Health Connect. Мы рассмотрим методы экземпляра HealthConnectManager
в последующих разделах. Поскольку HealthConnectManager предоставляет системную службу, вы не можете напрямую создать экземпляр этого класса и должны использовать getSystemService
. В частности, обратите внимание, что системная служба тесно связана с контекстом, в котором она создана, и не должна быть доступна вне этого контекста.
import android.health.connect.HealthConnectManager
...
val healthConnectManager: HealthConnectManager = requireNotNull(applicationContext.getSystemService(HealthConnectManager::class.java))
Обработка разрешений
Пользователь вашего приложения должен предоставить вашему приложению разрешение на доступ к данным Health Connect. Для этого запустите новое действие с указанными разрешениями и используйте полученный список предоставленных разрешений. Пользовательский интерфейс нового действия позволит пользователю выбирать разрешения, которые ему необходимо предоставить вашему приложению. Запрашивайте разрешения только для тех типов данных, которые вы планируете использовать в своем приложении.
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
.
Создание источников данных
Чтобы записать данные о состоянии здоровья в Health Connect, ваше приложение должно сначала создать источник данных для хранения информации. Источник данных обычно представляет собой конкретный 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"),
)
Запись записей
Подготовьте примеры записей FHIR в формате JSON. В сети существуют различные источники с примерами данных в формате 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
, API выдаст исключение IllegalArgumentException
. Если какой-либо запрос будет признан недействительным по каким-либо другим причинам, вызывающая сторона получит HealthConnectException
.
Уникальный ключ для конкретного запроса — это комбинация идентификатора источника данных, типа ресурса FHIR и идентификатора ресурса FHIR. Если эти три элемента в запросе соответствуют существующей записи, запускается обновление. В противном случае создается новая запись.
Чтение записей
Прочитайте записи по типу, а затем обработайте результаты по своему усмотрению.
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
}
Чтобы использовать API платформы Android для личных медицинских записей (PHR), необходимо:
- Настройте среду с помощью Android Studio
- Создание экземпляра
HealthConnectManager
- Обработка разрешений
- Создание источников данных
Настройте среду с помощью Android Studio
Чтобы иметь доступ к новейшим API, вам также необходим доступ к новейшим соответствующим инструментам:
- Установите последнюю версию Android Studio .
- В Android Studio выберите «Инструменты» > «Менеджер SDK» .
- На вкладке «Платформы SDK» выберите Android Baklava .
- Эта версия включает API уровня 36 и API PHR.
- На вкладке «Инструменты SDK» выберите последнюю доступную версию Android SDK Build-Tools .
- Нажмите «ОК», чтобы установить SDK.
- Используйте последнюю доступную версию плагина Android Gradle .
Объявить уровни API
Чтобы получить доступ к API PHR, вам необходимо выбрать соответствующую версию Android. Это делается путем объявления уровней API в app/build.gradle
.
...
compileSdk = 36
defaultConfig {
targetSdk = 36
...
Узнайте больше о настройке уровней API в элементе Uses-sdk-element и о настройке документации Android 16 SDK .
Объявить разрешения
Добавьте новые разрешения PHR в AndroidManifest.xml
. Объявляйте разрешения только для тех типов данных, которые вы собираетесь использовать в своем приложении. Полный список медицинских разрешений показан в блоке кода. Разрешения, не связанные с PHR, для Health Connect не включены.
...
<!-- 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
— это класс, отвечающий за обработку взаимодействий с разрешениями, а также за чтение и запись в локальный репозиторий данных Health Connect. Мы рассмотрим методы экземпляра HealthConnectManager
в последующих разделах. Поскольку HealthConnectManager предоставляет системную службу, вы не можете напрямую создать экземпляр этого класса и должны использовать getSystemService
. В частности, обратите внимание, что системная служба тесно связана с контекстом, в котором она создана, и не должна быть доступна вне этого контекста.
import android.health.connect.HealthConnectManager
...
val healthConnectManager: HealthConnectManager = requireNotNull(applicationContext.getSystemService(HealthConnectManager::class.java))
Обработка разрешений
Пользователь вашего приложения должен предоставить вашему приложению разрешение на доступ к данным Health Connect. Для этого запустите новое действие с указанными разрешениями и используйте полученный список предоставленных разрешений. Пользовательский интерфейс нового действия позволит пользователю выбирать разрешения, которые ему необходимо предоставить вашему приложению. Запрашивайте разрешения только для тех типов данных, которые вы планируете использовать в своем приложении.
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
.
Создание источников данных
Чтобы записать данные о состоянии здоровья в Health Connect, ваше приложение должно сначала создать источник данных для хранения информации. Источник данных обычно представляет собой конкретный 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"),
)
Запись записей
Подготовьте примеры записей FHIR в формате JSON. В сети существуют различные источники с примерами данных в формате 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
, API выдаст исключение IllegalArgumentException
. Если какой-либо запрос будет признан недействительным по каким-либо другим причинам, вызывающая сторона получит HealthConnectException
.
Уникальный ключ для конкретного запроса — это комбинация идентификатора источника данных, типа ресурса FHIR и идентификатора ресурса FHIR. Если эти три элемента в запросе соответствуют существующей записи, запускается обновление. В противном случае создается новая запись.
Чтение записей
Прочитайте записи по типу, а затем обработайте результаты по своему усмотрению.
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
}
Чтобы использовать API платформы Android для личных медицинских записей (PHR), необходимо:
- Настройте среду с помощью Android Studio
- Создание экземпляра
HealthConnectManager
- Обработка разрешений
- Создание источников данных
Настройте среду с помощью Android Studio
Чтобы иметь доступ к новейшим API, вам также необходим доступ к новейшим соответствующим инструментам:
- Установите последнюю версию Android Studio .
- В Android Studio выберите «Инструменты» > «Менеджер SDK» .
- На вкладке «Платформы SDK» выберите Android Baklava .
- Эта версия включает API уровня 36 и API PHR.
- На вкладке «Инструменты SDK» выберите последнюю доступную версию Android SDK Build-Tools .
- Нажмите «ОК», чтобы установить SDK.
- Используйте последнюю доступную версию плагина Android Gradle .
Объявить уровни API
Чтобы получить доступ к API PHR, вам необходимо выбрать соответствующую версию Android. Это делается путем объявления уровней API в app/build.gradle
.
...
compileSdk = 36
defaultConfig {
targetSdk = 36
...
Узнайте больше о настройке уровней API в элементе Uses-sdk-element и о настройке документации Android 16 SDK .
Объявить разрешения
Добавьте новые разрешения PHR в AndroidManifest.xml
. Объявляйте разрешения только для тех типов данных, которые вы собираетесь использовать в своем приложении. Полный список медицинских разрешений показан в блоке кода. Разрешения, не связанные с PHR, для Health Connect не включены.
...
<!-- 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
— это класс, отвечающий за обработку взаимодействий с разрешениями, а также за чтение и запись в локальный репозиторий данных Health Connect. Мы рассмотрим методы экземпляра HealthConnectManager
в последующих разделах. Поскольку HealthConnectManager предоставляет системную службу, вы не можете напрямую создать экземпляр этого класса и должны использовать getSystemService
. В частности, обратите внимание, что системная служба тесно связана с контекстом, в котором она создана, и не должна быть доступна вне этого контекста.
import android.health.connect.HealthConnectManager
...
val healthConnectManager: HealthConnectManager = requireNotNull(applicationContext.getSystemService(HealthConnectManager::class.java))
Обработка разрешений
Пользователь вашего приложения должен предоставить вашему приложению разрешение на доступ к данным Health Connect. Для этого запустите новое действие с указанными разрешениями и используйте полученный список предоставленных разрешений. Пользовательский интерфейс нового действия позволит пользователю выбирать разрешения, которые ему необходимо предоставить вашему приложению. Запрашивайте разрешения только для тех типов данных, которые вы планируете использовать в своем приложении.
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
.
Создание источников данных
Чтобы записать данные о состоянии здоровья в Health Connect, ваше приложение должно сначала создать источник данных для хранения информации. Источник данных обычно представляет собой конкретный 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"),
)
Запись записей
Подготовьте примеры записей FHIR в формате JSON. В сети существуют различные источники с примерами данных в формате 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
, API выдаст исключение IllegalArgumentException
. Если какой-либо запрос будет признан недействительным по каким-либо другим причинам, вызывающая сторона получит HealthConnectException
.
Уникальный ключ для конкретного запроса — это комбинация идентификатора источника данных, типа ресурса FHIR и идентификатора ресурса FHIR. Если эти три элемента в запросе соответствуют существующей записи, запускается обновление. В противном случае создается новая запись.
Чтение записей
Прочитайте записи по типу, а затем обработайте результаты по своему усмотрению.
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
}