เริ่มต้นใช้งาน PHR ใน API ของเฟรมเวิร์ก Android

หากต้องการใช้ API ของเฟรมเวิร์ก Android สําหรับบันทึกสุขภาพส่วนตัว (PHR) คุณต้องมีคุณสมบัติดังนี้

  1. กำหนดค่าสภาพแวดล้อมโดยใช้ Android Studio
  2. สร้างอินสแตนซ์ HealthConnectManager
  3. จัดการสิทธิ์
  4. สร้างแหล่งข้อมูล

กำหนดค่าสภาพแวดล้อมโดยใช้ Android Studio

หากต้องการเข้าถึง API เวอร์ชันล่าสุด คุณต้องมีสิทธิ์เข้าถึงเครื่องมือที่เกี่ยวข้องเวอร์ชันล่าสุดด้วย

  • ติดตั้ง Android Studio เวอร์ชันล่าสุด
  • ใน Android Studio ให้ไปที่เครื่องมือ > เครื่องมือจัดการ SDK
    • ในแท็บแพลตฟอร์ม SDK ให้เลือก Android Baklava
    • เวอร์ชันนี้รวม API ระดับ 36 และ PHR API
    • ในแท็บเครื่องมือ SDK ให้เลือก Android SDK Build-Tools เวอร์ชันล่าสุดที่ใช้ได้
    • คลิก "ตกลง" เพื่อติดตั้ง SDK
  • ใช้ปลั๊กอิน Android Gradle เวอร์ชันล่าสุดที่มี

ประกาศระดับ API

หากต้องการเข้าถึง PHR API คุณจะต้องกำหนดเป้าหมาย Android เวอร์ชันที่เหมาะสม ซึ่งทำได้โดยการประกาศระดับ API ใน app/build.gradle

...
compileSdk = 36

defaultConfig {
  targetSdk = 36
...

ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่าระดับ API ในเอกสารประกอบองค์ประกอบ uses-sdk และตั้งค่า SDK ของ Android 16

ประกาศสิทธิ์

เพิ่มสิทธิ์ 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 ที่ต้องใช้สิทธิ์ และไม่ได้ประกาศสิทธิ์นั้นในไฟล์ Manifest ของแอป ระบบจะแสดงข้อผิดพลาด 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 โดยให้เปิดใช้งานกิจกรรมใหม่ที่มีสิทธิ์ที่ระบุไว้ แล้วใช้รายการสิทธิ์ที่ได้รับ 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

สร้างแหล่งข้อมูล

หากต้องการเขียนข้อมูลสุขภาพลงใน 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 หากรายการทั้ง 3 รายการในคำขอตรงกับระเบียนที่มีอยู่ ระบบจะทริกเกอร์การอัปเดต มิเช่นนั้น ระบบจะสร้างระเบียนใหม่

อ่านระเบียน

อ่านระเบียนตามประเภท แล้วจัดการผลลัพธ์ตามที่เห็นสมควร

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
}