Android çerçeve API'lerinde PHR'yi kullanmaya başlama

Kişisel Sağlık Kayıtları (PHR) için Android çerçeve API'lerini kullanmak istiyorsanız:

  1. Android Studio'yu kullanarak ortamı yapılandırma
  2. HealthConnectManager örneği oluşturma
  3. İzinleri yönetme
  4. Veri kaynakları oluşturma

Android Studio'yu kullanarak ortamı yapılandırma

En son API'lere erişebilmek için ilgili en son araçlara da erişiminiz olmalıdır:

  • Android Studio'nun en son sürümünü yükleyin.
  • Android Studio'da Araçlar > SDK Yöneticisi'ne gidin.
    • SDK Platformları sekmesinde Android Baklava'yı seçin.
    • Bu sürüm, API düzeyi 36 ve PHR API'lerini içerir.
    • SDK Tools sekmesinde, mevcut en son Android SDK Build-Tools'u seçin.
    • SDK'yı yüklemek için Tamam'ı tıklayın.
  • Android Gradle eklentisinin mevcut en son sürümünü kullanın.

API düzeylerini belirtme

PHR API'lerine erişmek için Android'in uygun sürümünü hedeflemeniz gerekir. Bu işlem, app/build.gradle içinde API düzeyleri tanımlanarak yapılır.

...
compileSdk = 36

defaultConfig {
  targetSdk = 36
...

uses-sdk-element ve Android 16 SDK'sını ayarlama dokümanlarındaki API düzeylerini yapılandırma hakkında daha fazla bilgi edinin.

İzinleri beyan etme

AndroidManifest.xml'te yeni PHR izinleri ekleyin. Yalnızca uygulamanızda kullanmayı düşündüğünüz veri türleri için izinleri belirtin. Tıbbi konularla ilgili izinlerin tam listesi kod bloğunda gösterilir. Health Connect için PHR ile ilgili olmayan izinler dahil edilmez.

...
  <!--  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"/>
...

Bir uygulama, izin gerektiren bir API'yi kullanmaya çalışırsa ve bu izin uygulamanın manifest dosyasında belirtilmemişse SecurityException istisna atılır.

SecurityException türündeki bir istisna için exception.localizedMessage'te yararlı bir hata mesajı gösterilir.

HealthConnectManager sınıfını örneklendirme

HealthConnectManager, izin etkileşimlerinin yanı sıra Health Connect yerel veri deposunda okuma ve yazma işlemlerinden sorumlu sınıftır. HealthConnectManager sınıfının örnek yöntemlerini sonraki bölümlerde inceleyeceğiz. HealthConnectManager bir sistem hizmeti sunduğundan bu sınıfı doğrudan örneklendiremezsiniz ve getSystemService kullanmanız gerekir. Özellikle, sistem hizmetinin örneklendiği bağlamla sıkı bir şekilde ilişkili olduğunu ve bu bağlamın dışında erişilebilir hale getirilmemesi gerektiğini unutmayın.

import android.health.connect.HealthConnectManager
...
val healthConnectManager: HealthConnectManager = requireNotNull(applicationContext.getSystemService(HealthConnectManager::class.java))

İzinleri yönetme

Uygulamanızın kullanıcısı, uygulamanıza Health Connect verilerine erişme izni vermelidir. Bunu yapmak için, izinler belirtilmiş yeni bir etkinlik başlatın ve izin verilen izinlerin listesini kullanın. Yeni etkinliğin kullanıcı arayüzü, kullanıcının uygulamanıza vermesi gereken izinleri seçmesine olanak tanır. Yalnızca uygulamanızın kullanmasını istediğiniz veri türleri için izin isteyin.

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)

Gerekli izin olmadan okumaya veya yazmaya çalışmak HealthConnectException ile sonuçlanır.

HealthConnectException türündeki bir istisna için exception.localizedMessage'te yararlı bir hata mesajı gösterilir.

Veri kaynakları oluşturma

Health Connect'e sağlık verileri yazmak için uygulamanızın öncelikle bilgileri saklayacak bir veri kaynağı oluşturması gerekir. Veri kaynakları genellikle belirli bir API'yi veya tıbbi sistemi temsil eder.

Örnekte, My Hospital adlı bir veri kaynağı oluşturup FHIR sürümünü belirtiriz.

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"),
)

Kayıt yazma

JSON biçiminde örnek FHIR kayıtları hazırlayın. Web'de FHIR biçiminde örnek veriler içeren çeşitli kaynaklar vardır.

{
  "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"
      }
    }
  ]
}

Verileri ekleyin:

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, bağımsız değişken olarak bir UpsertMedicalResourceRequest listesi alır. Aramada iletilen UpsertMedicalResourceRequest öğelerinden herhangi biri eklenemezse UpsertMedicalResourceRequest listesinin tamamı için depoya hiçbir yazma işlemi yapılmaz.

Herhangi bir istek geçersiz MedicalDataSource kimlikleri içeriyorsa API bir IllegalArgumentException hatası oluşturur. Başka nedenlerle geçersiz kabul edilen istekler için arayana HealthConnectException gönderilir.

Belirli bir istek için benzersiz anahtar, veri kaynağı kimliği, FHIR kaynak türü ve FHIR kaynak kimliğinin birleşimidir. Bir istekteki bu üç öğe mevcut bir kayıtla eşleşirse güncelleme tetiklenir. Aksi takdirde yeni bir kayıt oluşturulur.

Kayıtları okuma

Kayıtları türe göre okuyun, ardından sonuçları uygun şekilde işleyin.

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
}