1. 始める前に
作成するアプリの概要
この Codelab では、FHIR Engine ライブラリを使用して Android アプリを作成します。アプリは FHIR Engine ライブラリを使用して、FHIR サーバーから FHIR リソースをダウンロードし、ローカルでの変更をサーバーにアップロードします。
学習内容
- Docker を使用してローカル HAPI FHIR サーバーを作成する方法
- FHIR Engine ライブラリを Android アプリに統合する方法
- Sync API を使用して、FHIR リソースをダウンロードしてアップロードする 1 回限りのジョブまたは定期的なジョブを設定する方法
- Search API の使用方法
- データアクセス API を使用して、FHIR リソースをローカルで作成、読み取り、更新、削除する方法
必要なもの
- Docker(Docker を入手)
- Android Studio の最新バージョン(v4.1.2 以降)
- Android Emulatorまたは Android 7.0 Nougat 以降を搭載した物理 Android デバイス
- サンプルコード
- Kotlin での Android 開発に関する基本的な知識
Android アプリを作成したことがない場合は、最初のアプリを作成することから始めます。
2. テストデータを使用してローカル HAPI FHIR サーバーを設定する
HAPI FHIR は、よく使用されているオープンソースの FHIR サーバーです。この Codelab では、Android アプリが接続するローカル HAPI FHIR サーバーを使用します。
ローカル HAPI FHIR サーバーを設定する
- ターミナルで次のコマンドを実行して、HAPI FHIR の最新イメージを取得します。
docker pull hapiproject/hapi:latest - Docker Desktop を使用して、以前にダウンロードしたイメージ
hapiproject/hapiを実行するか、次のコマンドを実行して、HAPI FHIR コンテナを作成します。 詳細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/ - すべての患者のテストデータをサーバーにアップロードするには、次を実行します。
ただし、完了までに時間がかかる場合があり、この Codelab では必要ありません。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というテキストと、検索結果として FHIR バンドルに患者データを含むページのResponse Bodyセクションが表示され、totalカウントが表示されます。
3. Android アプリを設定する
コードをダウンロードする
この Codelab のコードをダウンロードするには、Android FHIR SDK リポジトリのクローンを作成します。git clone https://github.com/ohs-foundation/android-fhir.git
この Codelab のスターター プロジェクトは codelabs/engine にあります。
Android Studio にアプリをインポートする
まず、スターター アプリを Android Studio にインポートします。
Android Studio を開き、[Import Project (Gradle, Eclipse ADT, etc.)] を選択して、codelabs/engine/ フォルダを先ほどダウンロードしたソースコードから選択します。

プロジェクトと Gradle ファイルを同期する
便宜上、FHIR Engine ライブラリの依存関係はすでにプロジェクトに追加されています。これにより、FHIR Engine ライブラリをアプリに統合できます。プロジェクトの app/build.gradle.kts ファイルの末尾に次の行を追加します。
dependencies {
// ...
implementation("com.google.android.fhir:engine:1.1.0")
}
すべての依存関係がアプリで使用可能であることを確認するには、この時点でプロジェクトを Gradle ファイルと同期する必要があります。
Android Studio のツールバーから [Sync Project with Gradle Files](
)を選択します。アプリをもう一度実行して、依存関係が正しく機能していることを確認することもできます。
スターター アプリを実行する
プロジェクトを Android Studio にインポートしたので、アプリを初めて実行する準備ができました。
Android Studio エミュレータを起動し、Android Studio のツールバーで [実行](
)をクリックします。

4. FHIR Engine インスタンスを作成する
FHIR Engine を Android アプリに組み込むには、FHIR Engine ライブラリを使用して FHIR Engine のインスタンスを開始する必要があります。以下の手順に沿って操作してください。
- Application クラスに移動します。この例では、
app/src/main/java/com/google/android/fhir/codelabs/engineにあるFhirApplication.ktです。 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: データベース エラーの戦略を決定します。この場合、開くときにエラーが発生するとデータベースが再作成されます。ServerConfigurationのbaseUrl: これは FHIR サーバーのベース URL です。指定された IP アドレス10.0.2.2は、Android エミュレータからアクセスできる localhost 用に特別に予約されています。詳細を学ぶ
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 Engine インスタンスを定義しました。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で、1 回限りの同期メカニズムを設定します。このコードを見つけて、triggerOneTimeSync()関数に追加します。 このコルーチンは、先ほど定義した AppFhirSyncWorker を使用して、FHIR サーバーとの 1 回限りの同期を開始します。同期プロセスの状態に基づいて 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 で変更ロジックを設定する
このセクションのコードは、PatientListViewModel の triggerUpdate 関数に追加されます。
- FHIR Engine にアクセスする:まず、FHIR Engine への参照を
PatientListViewModel.ktで取得します。 このコードは、ViewModel のスコープ内でコルーチンを起動し、FHIR Engine を初期化します。viewModelScope.launch { val fhirEngine = FhirApplication.fhirEngine(getApplication()) - Wakefield の患者を検索する:FHIR Engine を使用して、住所の市区町村が
Wakefieldの患者を検索します。 ここでは、FHIR Engine のval patientsFromWakefield = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Wakefield" } ) }searchメソッドを使用して、住所の市区町村に基づいて患者をフィルタリングしています。結果は、Wakefield の患者のリストになります。 - Taunton の患者を検索する:同様に、住所の市区町村が
Tauntonの患者を検索します。 これで、Wakefield と Taunton の 2 つの患者リストができました。val patientsFromTaunton = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Taunton" } ) } - 患者の住所を変更する:
patientsFromWakefieldリストの各患者を調べて、市区町村をTauntonに変更し、FHIR Engine で更新します。 同様に、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) } - 同期を開始する:データをローカルで変更したら、1 回限りの同期をトリガーして、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 サーバーにアップロードする方法
- FHIR Engine ライブラリを使用して Android アプリをビルドする方法
- FHIR Engine ライブラリで Sync API、Data Access API、Search API を使用する方法
次のステップ
- FHIR Engine ライブラリのドキュメントを確認する
- Search API の上級者向け機能を確認する
- FHIR Engine ライブラリを独自の Android アプリに適用する