1. ก่อนเริ่มต้น
สิ่งที่คุณจะสร้าง
ในโค้ดแล็บนี้ คุณจะได้สร้างแอป Android โดยใช้ไลบรารี FHIR Engine แอปของคุณจะใช้ FHIR Engine Library เพื่อดาวน์โหลดทรัพยากร FHIR จากเซิร์ฟเวอร์ FHIR และอัปโหลดการเปลี่ยนแปลงในเครื่องไปยังเซิร์ฟเวอร์
สิ่งที่คุณจะได้เรียนรู้
- วิธีสร้างเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่โดยใช้ Docker
- วิธีผสานรวมไลบรารี FHIR Engine เข้ากับแอปพลิเคชัน Android
- วิธีใช้ Sync API เพื่อตั้งค่างานแบบครั้งเดียวหรือเป็นระยะเพื่อดาวน์โหลดและอัปโหลดทรัพยากร FHIR
- วิธีใช้ Search API
- วิธีใช้ Data Access API เพื่อสร้าง อ่าน อัปเดต และลบทรัพยากร FHIR ในเครื่อง
สิ่งที่คุณต้องมี
- Docker (รับ Docker)
- Android Studio (v4.1.2 ขึ้นไป) เวอร์ชันล่าสุด
- Android Emulator หรืออุปกรณ์ Android ที่ใช้ Android 7.0 Nougat ขึ้นไป
- โค้ดตัวอย่าง
- ความรู้พื้นฐานเกี่ยวกับการพัฒนาแอป Android ใน Kotlin
หากไม่เคยสร้างแอป Android มาก่อน ให้เริ่มต้นด้วยการสร้างแอปแรก
2. ตั้งค่าเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่ด้วยข้อมูลทดสอบ
HAPI FHIR เป็นเซิร์ฟเวอร์ FHIR แบบโอเพนซอร์สที่ได้รับความนิยม เราใช้เซิร์ฟเวอร์ HAPI FHIR ในพื้นที่ในโค้ดแล็บเพื่อให้แอป Android เชื่อมต่อ
ตั้งค่าเซิร์ฟเวอร์ HAPI FHIR ในเครื่อง
- เรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัลเพื่อรับอิมเมจล่าสุดของ HAPI FHIR
docker pull hapiproject/hapi:latest
- สร้างคอนเทนเนอร์ HAPI FHIR โดยใช้ Docker Desktop เพื่อเรียกใช้อิมเมจที่ดาวน์โหลดไว้ก่อนหน้านี้
hapiproject/hapi
หรือเรียกใช้คําสั่งต่อไปนี้ ดูข้อมูลเพิ่มเติมdocker run -p 8080:8080 hapiproject/hapi:latest
- ตรวจสอบเซิร์ฟเวอร์โดยเปิด URL
http://localhost:8080/
ในเบราว์เซอร์ คุณควรเห็นเว็บอินเทอร์เฟซ HAPI FHIR
ป้อนข้อมูลทดสอบลงในเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่
เราต้องใช้ข้อมูลทดสอบบางอย่างในเซิร์ฟเวอร์เพื่อทดสอบแอปพลิเคชัน เราจะใช้ข้อมูลสังเคราะห์ที่ Synthea สร้างขึ้น
- ก่อนอื่น เราต้องดาวน์โหลดข้อมูลตัวอย่างจาก synthea-samples ดาวน์โหลดและแตกไฟล์
synthea_sample_data_fhir_r4_sep2019.zip
ข้อมูลตัวอย่างที่แตกไฟล์จะมีไฟล์.json
จำนวนมาก โดยแต่ละไฟล์จะเป็นกลุ่มธุรกรรมสำหรับผู้ป่วยแต่ละราย - เราจะอัปโหลดข้อมูลทดสอบสำหรับผู้ป่วย 3 รายไปยังเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่ เรียกใช้คําสั่งต่อไปนี้ในไดเรกทอรีที่มีไฟล์ JSON
curl -X POST -H "Content-Type: application/json" -d @./Aaron697_Brekke496_2fa15bc7-8866-461a-9000-f739e425860a.json http://localhost:8080/fhir/ curl -X POST -H "Content-Type: application/json" -d @./Aaron697_Stiedemann542_41166989-975d-4d17-b9de-17f94cb3eec1.json http://localhost:8080/fhir/ curl -X POST -H "Content-Type: application/json" -d @./Abby752_Kuvalis369_2b083021-e93f-4991-bf49-fd4f20060ef8.json http://localhost:8080/fhir/
- หากต้องการอัปโหลดข้อมูลทดสอบของผู้ป่วยทั้งหมดไปยังเซิร์ฟเวอร์ ให้เรียกใช้
แต่การดำเนินการนี้อาจใช้เวลานานและไม่จำเป็นต้องทำสำหรับโค้ดแล็บfor f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
- ยืนยันว่าข้อมูลทดสอบพร้อมใช้งานบนเซิร์ฟเวอร์โดยเปิด URL
http://localhost:8080/fhir/Patient/
ในเบราว์เซอร์ คุณควรเห็นข้อความHTTP 200 OK
และส่วนResponse Body
ของหน้าเว็บที่มีข้อมูลผู้ป่วยใน FHIR Bundle เป็นผลการค้นหาที่มีจำนวนtotal
3. ตั้งค่าแอป Android
ดาวน์โหลดรหัส
หากต้องการดาวน์โหลดโค้ดสําหรับโค้ดแล็บนี้ ให้โคลนที่เก็บ Android FHIR SDK: git clone https://github.com/google/android-fhir.git
โปรเจ็กต์เริ่มต้นสําหรับ Codelab นี้อยู่ใน codelabs/engine
นําเข้าแอปไปยัง Android Studio
เราเริ่มต้นด้วยการนําเข้าแอปเริ่มต้นไปยัง Android Studio
เปิด Android Studio เลือกนําเข้าโปรเจ็กต์ (Gradle, Eclipse ADT ฯลฯ) แล้วเลือกโฟลเดอร์ codelabs/engine/
จากซอร์สโค้ดที่คุณดาวน์โหลดไว้ก่อนหน้านี้
ซิงค์โปรเจ็กต์กับไฟล์ Gradle
เราได้เพิ่มไลบรารี FHIR Engine ลงในโปรเจ็กต์แล้วเพื่อความสะดวก ซึ่งจะช่วยให้คุณผสานรวมไลบรารี FHIR Engine ในแอปได้ โปรดดูบรรทัดต่อไปนี้จนถึงส่วนท้ายของไฟล์ app/build.gradle.kts
ของโปรเจ็กต์
dependencies {
// ...
implementation("com.google.android.fhir:engine:1.1.0")
}
คุณควรซิงค์โปรเจ็กต์กับไฟล์ Gradle ในตอนนี้เพื่อให้แน่ใจว่าแอปของคุณจะใช้ Dependency ทั้งหมดได้
เลือกซิงค์โปรเจ็กต์กับไฟล์ Gradle () จากแถบเครื่องมือของ Android Studio นอกจากนี้ คุณยังเรียกใช้แอปอีกครั้งเพื่อตรวจสอบว่า Dependency ทํางานอย่างถูกต้องหรือไม่
เรียกใช้แอปเริ่มต้น
เมื่อนําเข้าโปรเจ็กต์ไปยัง Android Studio แล้ว คุณก็พร้อมเรียกใช้แอปเป็นครั้งแรก
เริ่มโปรแกรมจำลอง Android Studio แล้วคลิก "เรียกใช้" () ในแถบเครื่องมือของ Android Studio
4. สร้างอินสแตนซ์ FHIR Engine
หากต้องการรวม FHIR Engine ไว้ในแอป Android คุณจะต้องใช้ไลบรารี FHIR Engine และเริ่มต้นอินสแตนซ์ของ FHIR Engine ขั้นตอนที่ระบุไว้ด้านล่างจะช่วยแนะนำคุณตลอดกระบวนการ
- ไปที่คลาสแอปพลิเคชัน ซึ่งในตัวอย่างนี้คือ
FhirApplication.kt
ซึ่งอยู่ในapp/src/main/java/com/google/android/fhir/codelabs/engine
- ในเมธอด
onCreate()
ให้เพิ่มโค้ดต่อไปนี้เพื่อเริ่มต้น FHIR Engine หมายเหตุ:FhirEngineProvider.init( FhirEngineConfiguration( enableEncryptionIfSupported = true, RECREATE_AT_OPEN, ServerConfiguration( baseUrl = "http://10.0.2.2:8080/fhir/", httpLogger = HttpLogger( HttpLogger.Configuration( if (BuildConfig.DEBUG) HttpLogger.Level.BODY else HttpLogger.Level.BASIC, ), ) { Log.d("App-HttpLog", it) }, ), ), )
enableEncryptionIfSupported
: เปิดใช้การเข้ารหัสข้อมูลหากอุปกรณ์รองรับRECREATE_AT_OPEN
: กําหนดกลยุทธ์ข้อผิดพลาดของฐานข้อมูล ในกรณีนี้ ระบบจะสร้างฐานข้อมูลขึ้นมาใหม่หากเกิดข้อผิดพลาดเมื่อเปิดbaseUrl
ในServerConfiguration
: นี่คือ URL ฐานของเซิร์ฟเวอร์ FHIR ที่อยู่ IP10.0.2.2
ที่ระบุไว้เป็นการจองไว้สำหรับ localhost โดยเฉพาะ ซึ่งเข้าถึงได้จากโปรแกรมจำลอง Android ดูข้อมูลเพิ่มเติม
- ในคลาส
FhirApplication
ให้เพิ่มบรรทัดต่อไปนี้เพื่อสร้างอินสแตนซ์ FHIR Engine แบบล่าช้า การดำเนินการนี้ช่วยให้มั่นใจว่าอินสแตนซ์ FhirEngine จะสร้างขึ้นเมื่อมีการเข้าถึงเป็นครั้งแรกเท่านั้น ไม่ใช่ทันทีที่แอปเริ่มทำงานprivate val fhirEngine: FhirEngine by lazy { FhirEngineProvider.getInstance(this) }
- เพิ่มเมธอดที่สะดวกต่อไปนี้ในคลาส
FhirApplication
เพื่อให้เข้าถึงได้ง่ายขึ้นทั่วทั้งแอปพลิเคชัน เมธอดแบบคงที่นี้ช่วยให้คุณเรียกข้อมูลอินสแตนซ์ FHIR Engine ได้จากทุกที่ในแอปโดยใช้บริบทcompanion object { fun fhirEngine(context: Context) = (context.applicationContext as FhirApplication).fhirEngine }
5. ซิงค์ข้อมูลกับเซิร์ฟเวอร์ FHIR
- สร้างชั้นเรียนใหม่
DownloadWorkManagerImpl.kt
ในคลาสนี้ คุณจะได้กำหนดวิธีที่แอปพลิเคชันจะดึงข้อมูลทรัพยากรรายการถัดไปจากรายการเพื่อดาวน์โหลด คลาสนี้มีคิวประเภททรัพยากรที่ต้องการดาวน์โหลด โดยจะประมวลผลคำตอบและดึงทรัพยากรจากแพ็กเกจที่ส่งคืน ซึ่งจะบันทึกไว้ในฐานข้อมูลในเครื่องclass DownloadWorkManagerImpl : DownloadWorkManager { private val urls = LinkedList(listOf("Patient")) override suspend fun getNextRequest(): DownloadRequest? { val url = urls.poll() ?: return null return DownloadRequest.of(url) } override suspend fun getSummaryRequestUrls() = mapOf<ResourceType, String>() override suspend fun processResponse(response: Resource): Collection<Resource> { var bundleCollection: Collection<Resource> = mutableListOf() if (response is Bundle && response.type == Bundle.BundleType.SEARCHSET) { bundleCollection = response.entry.map { it.resource } } return bundleCollection } }
- สร้างคลาสใหม่
AppFhirSyncWorker.kt
คลาสนี้จะกำหนดวิธีที่แอปจะซิงค์กับเซิร์ฟเวอร์ FHIR ระยะไกลโดยใช้โปรแกรมทำงานเบื้องหลัง ในส่วนนี้ เราได้กำหนดเครื่องมือจัดการการดาวน์โหลด ตัวแก้ไขความขัดแย้ง และอินสแตนซ์เครื่องมือ FHIR ที่จะใช้สําหรับการซิงค์class AppFhirSyncWorker(appContext: Context, workerParams: WorkerParameters) : FhirSyncWorker(appContext, workerParams) { override fun getDownloadWorkManager() = DownloadWorkManagerImpl() override fun getConflictResolver() = AcceptLocalConflictResolver override fun getFhirEngine() = FhirApplication.fhirEngine(applicationContext) override fun getUploadStrategy() = UploadStrategy.forBundleRequest( methodForCreate = HttpCreateMethod.PUT, methodForUpdate = HttpUpdateMethod.PATCH, squash = true, bundleSize = 500, ) }
- ใน ViewModel
PatientListViewModel.kt
คุณจะตั้งค่ากลไกการซิงค์แบบครั้งเดียว ค้นหาและเพิ่มโค้ดนี้ลงในฟังก์ชันtriggerOneTimeSync()
โคโรทีนนี้จะเริ่มการซิงค์แบบครั้งเดียวกับเซิร์ฟเวอร์ FHIR โดยใช้ AppFhirSyncWorker ที่เรากําหนดไว้ก่อนหน้านี้ จากนั้นระบบจะอัปเดต UI ตามสถานะของกระบวนการซิงค์viewModelScope.launch { Sync.oneTimeSync<AppFhirSyncWorker>(getApplication()) .shareIn(this, SharingStarted.Eagerly, 10) .collect { _pollState.emit(it) } }
- ในไฟล์
PatientListFragment.kt
ให้อัปเดตเนื้อหาของฟังก์ชันhandleSyncJobStatus
ดังนี้ เมื่อกระบวนการซิงค์เสร็จสมบูรณ์แล้ว ข้อความแจ้งเตือนจะปรากฏขึ้นเพื่อแจ้งให้ผู้ใช้ทราบ จากนั้นแอปจะแสดงผู้ป่วยทั้งหมดโดยการเรียกใช้การค้นหาที่มีชื่อว่างwhen (syncJobStatus) { is SyncJobStatus.Finished -> { Toast.makeText(requireContext(), "Sync Finished", Toast.LENGTH_SHORT).show() viewModel.searchPatientsByName("") } else -> {} }
เมื่อตั้งค่าทุกอย่างเรียบร้อยแล้ว ให้เรียกใช้แอปโดยคลิกปุ่ม Sync
ในเมนู หากทุกอย่างทำงานได้อย่างถูกต้อง คุณควรเห็นการดาวน์โหลดและแสดงผู้ป่วยจากเซิร์ฟเวอร์ FHIR ในพื้นที่ในแอปพลิเคชัน
6. แก้ไขและอัปโหลดข้อมูลผู้ป่วย
ในส่วนนี้ เราจะแนะนำขั้นตอนการแก้ไขข้อมูลผู้ป่วยตามเกณฑ์ที่เฉพาะเจาะจง และการอัปโหลดข้อมูลที่อัปเดตไปยังเซิร์ฟเวอร์ FHIR กล่าวโดยละเอียดคือ เราจะสลับเมืองที่อยู่สำหรับผู้ป่วยที่อาศัยอยู่ใน Wakefield
และ Taunton
ขั้นตอนที่ 1: ตั้งค่าตรรกะการแก้ไขใน PatientListViewModel
โค้ดในส่วนนี้จะเพิ่มลงในฟังก์ชัน triggerUpdate
ใน PatientListViewModel
- เข้าถึง FHIR Engine:เริ่มต้นด้วยการอ้างอิง FHIR Engine ใน
PatientListViewModel.kt
โค้ดนี้จะเปิด coroutine ภายในขอบเขตของ ViewModel และเริ่มต้นการทำงานของเครื่องมือ FHIRviewModelScope.launch { val fhirEngine = FhirApplication.fhirEngine(getApplication())
- ค้นหาผู้ป่วยจาก Wakefield:ใช้เครื่องมือ FHIR เพื่อค้นหาผู้ป่วยที่มีเมืองในที่อยู่เป็น
Wakefield
ในส่วนนี้ เราใช้เมธอดval patientsFromWakefield = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Wakefield" } ) }
search
ของเครื่องมือ FHIR เพื่อกรองผู้ป่วยตามเมืองที่อยู่ ผลลัพธ์ที่ได้จะเป็นรายการผู้ป่วยจากเวย์คอล์ด - ค้นหาผู้ป่วยจาก Taunton:ในทํานองเดียวกัน ให้ค้นหาผู้ป่วยที่มีเมืองในที่อยู่เป็น
Taunton
ตอนนี้เรามีรายชื่อผู้ป่วย 2 รายการ รายการหนึ่งจาก Wakefield และอีกรายการหนึ่งจาก Tauntonval patientsFromTaunton = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Taunton" } ) }
- แก้ไขที่อยู่ผู้ป่วย:ตรวจสอบผู้ป่วยแต่ละรายในรายการ
patientsFromWakefield
เปลี่ยนเมืองเป็นTaunton
และอัปเดตในเครื่องมือ FHIR ในทํานองเดียวกัน ให้อัปเดตผู้ป่วยแต่ละรายในรายการpatientsFromWakefield.forEach { it.resource.address.first().city = "Taunton" fhirEngine.update(it.resource) }
patientsFromTaunton
ให้เปลี่ยนเมืองเป็นWakefield
patientsFromTaunton.forEach { it.resource.address.first().city = "Wakefield" fhirEngine.update(it.resource) }
- เริ่มการซิงค์:หลังจากแก้ไขข้อมูลในเครื่องแล้ว ให้เรียกใช้การซิงค์แบบครั้งเดียวเพื่อให้มั่นใจว่าข้อมูลได้รับการอัปเดตในเซิร์ฟเวอร์ FHIR
วงเล็บปิดtriggerOneTimeSync() }
}
หมายถึงจุดสิ้นสุดของโคโรทีนซึ่งเปิดใช้งานตั้งแต่ต้น
ขั้นตอนที่ 2: ทดสอบฟังก์ชันการทำงาน
- การทดสอบ UI:เรียกใช้แอป คลิกปุ่ม
Update
ในเมนู คุณควรเห็นเมืองที่อยู่ของผู้ป่วยAaron697
และAbby752
สลับที่กัน - การยืนยันเซิร์ฟเวอร์:เปิดเบราว์เซอร์แล้วไปที่
http://localhost:8080/fhir/Patient/
ยืนยันว่าเมืองในที่อยู่ของผู้ป่วยAaron697
และAbby752
ได้รับการอัปเดตในเซิร์ฟเวอร์ FHIR ในพื้นที่แล้ว
เมื่อทำตามขั้นตอนเหล่านี้ แสดงว่าคุณได้ติดตั้งใช้งานกลไกในการแก้ไขข้อมูลผู้ป่วยและซิงค์การเปลี่ยนแปลงกับเซิร์ฟเวอร์ FHIR เรียบร้อยแล้ว
7. ค้นหาผู้ป่วยตามชื่อ
การค้นหาผู้ป่วยด้วยชื่อเป็นวิธีที่เรียกข้อมูลได้แบบใช้งานง่าย เราจะอธิบายขั้นตอนการใช้งานฟีเจอร์นี้ในแอปพลิเคชันของคุณ
ขั้นตอนที่ 1: อัปเดตลายเซ็นฟังก์ชัน
ไปที่ไฟล์ PatientListViewModel.kt
แล้วค้นหาฟังก์ชันชื่อ searchPatientsByName
เราจะเพิ่มโค้ดลงในฟังก์ชันนี้
หากต้องการกรองผลลัพธ์ตามการค้นหาชื่อที่ระบุ และแสดงผลลัพธ์เพื่อให้ UI อัปเดต ให้รวมบล็อกโค้ดแบบมีเงื่อนไขต่อไปนี้
viewModelScope.launch {
val fhirEngine = FhirApplication.fhirEngine(getApplication())
if (nameQuery.isNotEmpty()) {
val searchResult = fhirEngine.search<Patient> {
filter(
Patient.NAME,
{
modifier = StringFilterModifier.CONTAINS
value = nameQuery
},
)
}
liveSearchedPatients.value = searchResult.map { it.resource }
}
}
ในกรณีนี้ หาก nameQuery
ไม่ได้ว่างเปล่า ฟังก์ชันการค้นหาจะกรองผลลัพธ์ให้แสดงเฉพาะผู้ป่วยที่มีชื่อตรงกับคำค้นหาที่ระบุ
ขั้นตอนที่ 2: ทดสอบฟังก์ชันการค้นหาใหม่
- เปิดแอปอีกครั้ง:หลังจากทําการเปลี่ยนแปลงเหล่านี้แล้ว ให้สร้างและเรียกใช้แอปอีกครั้ง
- ค้นหาผู้ป่วย: ใช้ฟังก์ชันการค้นหาในหน้าจอรายชื่อผู้ป่วย ตอนนี้คุณควรป้อนชื่อ (หรือชื่อบางส่วน) เพื่อกรองรายการผู้ป่วยได้ตามต้องการ
เมื่อทำตามขั้นตอนเหล่านี้เสร็จแล้ว คุณได้ปรับปรุงแอปพลิเคชันโดยให้ผู้ใช้ค้นหาผู้ป่วยตามชื่อได้อย่างมีประสิทธิภาพ ซึ่งจะช่วยปรับปรุงประสบการณ์ของผู้ใช้และประสิทธิภาพในการดึงข้อมูลได้อย่างมาก
8. ยินดีด้วย
คุณใช้ไลบรารี FHIR Engine เพื่อจัดการทรัพยากร FHIR ในแอป
- ใช้ Sync API เพื่อซิงค์ทรัพยากร FHIR กับเซิร์ฟเวอร์ FHIR
- ใช้ Data Access API เพื่อสร้าง อ่าน อัปเดต และลบทรัพยากร FHIR ในพื้นที่
- ใช้ Search API เพื่อค้นหาทรัพยากร FHIR ในพื้นที่
สิ่งที่เราได้พูดถึง
- วิธีตั้งค่าเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่
- วิธีอัปโหลดข้อมูลทดสอบไปยังเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่
- วิธีสร้างแอป Android โดยใช้ FHIR Engine Library
- วิธีใช้ Sync API, Data Access API และ Search API ในไลบรารี FHIR Engine
ขั้นตอนถัดไป
- สำรวจเอกสารประกอบสำหรับไลบรารี FHIR Engine
- สำรวจฟีเจอร์ขั้นสูงของ Search API
- ใช้ FHIR Engine Library ในแอป Android ของคุณเอง