כדי להשתמש בממשקי ה-API של Android framework עבור רשומות בריאות אישיות (PHR), צריך:
- הגדרת הסביבה באמצעות Android Studio
- יצירת מכונה של
HealthConnectManager
- טיפול בהרשאות
- יצירת מקורות נתונים
הגדרת הסביבה באמצעות Android Studio
כדי לקבל גישה לממשקי ה-API העדכניים ביותר, צריך גם גישה לכלים הרלוונטיים העדכניים ביותר:
- מתקינים את הגרסה האחרונה של Android Studio.
- ב-Android Studio, עוברים אל Tools (כלים) > SDK Manager (מנהל SDK).
- בכרטיסייה SDK Platforms (פלטפורמות SDK), בוחרים באפשרות Android Baklava.
- הגרסה הזו כוללת ממשקי API ברמה 36 וממשקי PHR API.
- בכרטיסייה SDK Tools, בוחרים את 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
.
כדאי להצהיר על הרשאות רק לגבי סוגי הנתונים שבהם אתם מתכוונים להשתמש באפליקציה. הרשימה המלאה של ההרשאות שקשורות לתחום הרפואה מופיעה בבלוק הקוד. ההרשאות של Health Connect שלא קשורות ל-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
היא הכיתה שאחראית לטיפול באינטראקציות של הרשאות, וגם לקריאה ולכתיבה במאגר הנתונים המקומי של 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
}