次の例は、一般的なワークフローの一環として元データを読み取る方法を示しています。
データを読み取る
ヘルスコネクトでは、アプリがフォアグラウンドとバックグラウンドにあるときに、アプリがデータストアからデータを読み取ることができます。
- フォアグラウンドの読み取り: 通常、アプリがフォアグラウンドにある場合は、ヘルスコネクトからデータを読み取ることができます。このような場合は、読み取りオペレーション中にユーザーまたはシステムがアプリをバックグラウンドに配置した場合に、フォアグラウンド サービスを使用してこのオペレーションを実行することを検討してください。 
- バックグラウンドでの読み取り: ユーザーに追加の権限をリクエストすることで、ユーザーまたはシステムがアプリをバックグラウンドに配置した後もデータを読み取ることができます。完全なバックグラウンド読み取りの例をご覧ください。 
ヘルスコネクトのデータの種類「歩数」では、各読み取りの間にユーザーが歩いた歩数が記録されます。歩数は、健康、フィットネス、ウェルネスのプラットフォームで共通の測定値を表します。ヘルスコネクトでは、歩数データの読み取りと書き込みが簡単にできます。
レコードを読み取るには、ReadRecordsRequest を作成し、readRecords の呼び出し時に指定します。
次の例は、特定期間内のユーザーの歩数データを読み取る方法を示しています。SensorManager を使用した拡張例については、歩数のデータガイドをご覧ください。
suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.readRecords(
            ReadRecordsRequest(
                StepsRecord::class,
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        for (record in response.records) {
            // Process each record
        }
    } catch (e: Exception) {
        // Run error handling here
    }
}
また、aggregate を使用して、データを集計して読み取ることもできます。
suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}
バックグラウンドでの読み取りの例
バックグラウンドでデータを読み取るには、マニフェスト ファイルで次の権限を宣言します。
<application>
  <uses-permission android:name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND" />
...
</application>
次の例は、WorkManager を使用して、特定期間内のユーザーの歩数データをバックグラウンドで読み取る方法を示しています。
class ScheduleWorker(private val appContext: Context, workerParams: WorkerParameters):
    CoroutineWorker(appContext, workerParams) {
    override suspend fun doWork(): Result {
        // Read data and process it.
        ...
        // Return success indicating successful data retrieval
        return Result.success()
    }
}
if (healthConnectClient
    .features
    .getFeatureStatus(
    HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
    ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
    // Check if necessary permission is granted
    val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()
    if (PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND !in grantedPermissions) {
        // Perform read in foreground
        ...
    } else {
        // Schedule the periodic work request in background
        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()
        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
} else {
  // Background reading is not available, perform read in foreground
  ...
}
ReadRecordsRequest パラメータのデフォルトの pageSize 値は 1000 です。単一の readResponse のレコード数がリクエストの pageSize を超える場合は、pageToken を使用してレスポンスのすべてのページを反復処理し、すべてのレコードを取得する必要があります。ただし、レート制限に関する問題が発生しないように注意してください。
pageToken の読み取りの例
リクエストされた期間のすべての利用可能なデータを取得するには、レコードの読み取りに pageToken を使用することをおすすめします。
次の例は、すべてのページトークンが使い果たされるまで、すべてのレコードを読み取る方法を示しています。
val type = HeartRateRecord::class
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofDays(7))
try {
    var pageToken: String? = null
    do {
        val readResponse =
            healthConnectClient.readRecords(
                ReadRecordsRequest(
                    recordType = type,
                    timeRangeFilter = TimeRangeFilter.between(
                        startTime,
                        endTime
                    ),
                    pageToken = pageToken
                )
            )
        val records = readResponse.records
        // Do something with records
        pageToken = readResponse.pageToken
    } while (pageToken != null)
} catch (quotaError: IllegalStateException) {
    // Backoff
}
大規模なデータセットを読み取る際のベスト プラクティスについては、レート制限を回避する計画をご覧ください。
以前に書き込まれたデータを読み取る
アプリが以前にヘルスコネクトにレコードを書き込んでいた場合は、そのアプリで履歴データを読み取ることができます。これは、ユーザーによるインストール後にヘルスコネクトと再同期する必要があるシナリオに該当します。
読み取りにはいくつかの制限があります。
- Android 14 以降の場合 - アプリが独自のデータを読み取る際の履歴制限はありません。
- アプリが他のデータを読み取る際の 30 日間の制限。
 
- Android 13 以前の場合 - アプリがデータを読み取る際の 30 日間の制限。
 
制限は、読み取り権限をリクエストすることで解除できます。
履歴データを読み取るには、ReadRecordsRequest の dataOriginFilter パラメータで、パッケージ名を DataOrigin オブジェクトとして指定する必要があります。
次の例は、心拍数レコードを読み取るときにパッケージ名を指定する方法を示しています。
try {
    val response =  healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = HeartRateRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
            dataOriginFilter = setOf(DataOrigin("com.my.package.name"))
        )
    )
    for (record in response.records) {
        // Process each record
    }
} catch (e: Exception) {
    // Run error handling here
}
30 日以上前のデータを読み取る
デフォルトでは、すべてのアプリは、最初に権限が付与された日の 30 日前までのデータをヘルスコネクトから読み取ることができます。
デフォルトの制限を超えて読み取り権限を拡張する必要がある場合は、PERMISSION_READ_HEALTH_DATA_HISTORY をリクエストします。この権限がない場合、30 日より古いレコードを読み取ろうとするとエラーが発生します。
削除されたアプリの権限の履歴
ユーザーがアプリを削除すると、履歴権限を含むすべての権限が取り消されます。ユーザーがアプリを再インストールして権限を再度付与すると、同じデフォルトの制限が適用され、アプリは新しい日付から最大 30 日間遡ってヘルスコネクトからデータを読み取ることができます。
たとえば、ユーザーが 2023 年 5 月 10 日にアプリを削除し、2023 年 5 月 15 日にアプリを再インストールして読み取り権限を付与したとします。この場合、アプリはデフォルトで 2023 年 4 月 15 日以降のデータを読み取れます。
例外を処理する
ヘルスコネクトは、問題が発生した場合に CRUD 操作について標準的な例外をスローします。すべてのアプリで、これらを適切にキャッチして処理する必要があります。
HealthConnectClient の各メソッドは、スローされる可能性のある例外をリストしますが、一般に次のような処理が必要です。
| 例外 | 説明 | 推奨されるベスト プラクティス | 
|---|---|---|
| IllegalStateException | 次のいずれかの状況が発生した場合にスローされます。 
 | リクエストを処理する前に、入力に関する潜在的な問題に対処します。リクエストで値を直接使用する代わりに、カスタム関数内で変数に値を代入するか、パラメータとして使用することをおすすめします。そうすることで、エラー処理戦略を適用できます。 | 
| IOException | ディスクのデータの読み取りと書き込みで問題が発生した場合にスローされます。 | この問題を回避するには、次の方法をお試しください。 
 | 
| RemoteException | SDK が接続されている基となるサービスでエラーが発生したか、サービスとの通信中にエラーが発生した場合にスローされます。 たとえば、アプリが特定の uidを持つレコードを削除しようとした場合に、基となるサービスでチェックして初めてレコードが存在しないことが検出されると、例外がスローされます。 | この問題を回避するには、次の方法をお試しください。 
 | 
| SecurityException | 現在付与されていない権限を必要とするリクエストの場合にスローされます。 | この問題を回避するには、公開したアプリのヘルスコネクトのデータタイプの使用を宣言していることを確認します。また、ヘルスコネクトの権限は、マニフェスト ファイルとアクティビティ内で宣言する必要があります。 | 
