1. 始める前に
作成するアプリの概要
この Codelab では、FHIR Engine ライブラリを使用して Android アプリを作成します。アプリは FHIR エンジン ライブラリを使用して、FHIR サーバーから FHIR リソースをダウンロードし、ローカルの変更をサーバーにアップロードします。
学習内容
- Docker を使用してローカル HAPI FHIR サーバーを作成する方法
- FHIR Engine ライブラリを Android アプリケーションに統合する方法
- Sync API を使用して、FHIR リソースをダウンロードしてアップロードする 1 回限りのジョブまたは定期的なジョブを設定する方法
- 検索 API の使用方法
- Data Access API を使用して FHIR リソースをローカルで作成、読み取り、更新、削除する方法
必要なもの
- Docker(Docker を入手)
- 最新バージョンの Android Studio(v4.1.2 以降)
- Android エミュレータ、または 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/google/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 のインスタンスを初期化する必要があります。以下の手順に沿って操作してください。
- アプリケーション クラス(この例では
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は 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)で、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 にアクセスする:まず、
PatientListViewModel.ktで FHIR Engine への参照を取得します。 このコードは、ViewModel のスコープ内でコルーチンを起動し、FHIR エンジンを初期化します。viewModelScope.launch { val fhirEngine = FhirApplication.fhirEngine(getApplication()) - Wakefield の患者を検索する:FHIR エンジンを使用して、住所の都市が
Wakefieldの患者を検索します。 ここでは、FHIR エンジンのval patientsFromWakefield = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Wakefield" } ) }searchメソッドを使用して、住所の市区町村に基づいて患者をフィルタリングしています。結果として、Wakefield の患者のリストが返されます。 - トーントンに住む患者を検索する:同様に、住所の市区町村が
Tauntonの患者を検索します。 これで、ウェイクフィールドとトーントンの 2 つの患者リストができました。val 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) } - 同期を開始する:データをローカルで変更した後、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 エンジン ライブラリを使用して、アプリで 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 の高度な機能を試す
- 独自の Android アプリで FHIR Engine ライブラリを適用する