元データを読み取る

次の例は、一般的なワークフローの一環として元データを読み取る方法を示しています。

データを読み取る

ヘルスコネクトでは、アプリがフォアグラウンドとバックグラウンドにあるときに、アプリがデータストアからデータを読み取ることができます。

  • フォアグラウンドの読み取り: 通常、アプリがフォアグラウンドにある場合は、ヘルスコネクトからデータを読み取ることができます。このような場合は、読み取りオペレーション中にユーザーまたはシステムがアプリをバックグラウンドに配置した場合に、フォアグラウンド サービスを使用してこのオペレーションを実行することを検討してください。

  • バックグラウンドでの読み取り: ユーザーに追加の権限をリクエストすることで、ユーザーまたはシステムがアプリをバックグラウンドに配置した後もデータを読み取ることができます。完全なバックグラウンド読み取りの例をご覧ください。

ヘルスコネクトのデータの種類「歩数」では、各読み取りの間にユーザーが歩いた歩数が記録されます。歩数は、健康、フィットネス、ウェルネスのプラットフォームで共通の測定値を表します。ヘルスコネクトでは、歩数データの読み取りと書き込みができます。

レコードを読み取るには、ReadRecordsRequest を作成し、readRecords の呼び出し時に指定します。

次の例は、特定期間内のユーザーの歩数データを読み取る方法を示しています。SensorManager を使用した拡張例については、歩数のデータガイドをご覧ください。

val response = healthConnectClient.readRecords(
    ReadRecordsRequest(
        HeartRateRecord::class,
        timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
    )
)
response.records.forEach { record ->
    /* Process records */
}

また、aggregate を使用して、データを集計して読み取ることもできます。

suspend fun readStepsAggregate(startTime: Instant, endTime: Instant): Long {
    val response = healthConnectClient.aggregate(
        AggregateRequest(
            metrics = setOf(StepsRecord.COUNT_TOTAL),
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
    return response[StepsRecord.COUNT_TOTAL] ?: 0L
}

モバイルの歩数を読み取る

Android 14(API レベル 34)と SDK 拡張機能バージョン 20 以降では、ヘルスコネクトがデバイス上の歩数計を提供します。アプリに READ_STEPS 権限が付与されている場合、ヘルスコネクトは Android 搭載デバイスから歩数の取得を開始し、ユーザーはヘルスコネクトの [歩数] エントリに歩数データが自動的に追加されるのを確認できます。

デバイス上の歩数計が利用可能かどうかを確認するには、デバイスが Android 14(API レベル 34)を搭載し、SDK 拡張機能バージョン 20 以上を備えていることを確認します。

val isStepTrackingAvailable =
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
        SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20

アプリが aggregate を使用して歩数の合計を読み取り、DataOrigin でフィルタリングしていない場合、デバイス上の歩数は自動的に合計に含まれるため、2026 年 6 月のアップデートで変更は必要ありません。

デバイス上のステップのアトリビューションの変更

2026 年 6 月のアップデート以降、ヘルスコネクトでネイティブにトラッキングされた歩数は、合成パッケージ名(SPN)com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e など)に帰属します。

以前は、組み込みステップはパッケージ名 android に関連付けられていました。2026 年 6 月より前に記録された過去の歩数データは、android パッケージ名を保持します。

SPN はデバイス固有であり、ユーザーのプライバシーを保護するためにアプリケーションごとにスコープが設定されています。

  • 安定: 現在のデバイスの SPN は、アプリにとって安定しています。
  • アプリケーション スコープ: 同じデバイス上の異なるアプリケーションは、デバイス上の歩数データに対して異なる SPN を認識します。

オンデバイスの歩数をクエリする

SPN はスコープが設定され、デバイス固有であるため、SPN 値をハードコードしてはなりません。代わりに、getCurrentDeviceDataSource() API を使用して現在のデバイスの SPN を取得します。

デバイス上の歩数計には SDK 拡張機能バージョン 20 以降が必要ですが、getCurrentDeviceDataSource() API は SDK 拡張機能バージョン 11 以降を搭載した Android 14(API レベル 34)で利用できます。

getCurrentDeviceDataSource() API は、ヘルスコネクト Jetpack ライブラリではまだ利用できません。次の例では、代わりに Android フレームワーク API を使用します。

import android.content.Context
import android.health.connect.HealthConnectManager

val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
val deviceDataSource = healthConnectManager?.getCurrentDeviceDataSource()
val currentDeviceSpn = deviceDataSource?.deviceDataOrigin?.packageName

アプリがデバイス上の歩数を読み取る必要がある場合、またはソースアプリやデバイスごとに歩数データを表示する場合は、DataOriginandroid または デバイスの SPN と一致するレコードをクエリする必要があります。アプリで歩数データの帰属を表示する場合は、metadata.device を使用して個々のレコードのソースデバイスを特定します。集計データ内の SPN で識別されるオンデバイスの手順については、DeviceDataSourcemodelmanufacturer などのデバイス メタデータを使用してアトリビューションを行うか、「スマートフォン」などの一般的なラベルをオンデバイスの手順に使用できます。

次の例は、android と現在のデバイス SPN の両方でフィルタリングして、デバイス上の集計された歩数データを読み取る方法を示しています。

import android.content.Context
import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.ext.SdkExtensions
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.metadata.DataOrigin
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.time.TimeRangeFilter
import java.time.Instant

suspend fun readDeviceStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    context: Context,
    startTime: Instant,
    endTime: Instant
) {
    // 1. Check if SDK Extension 11+ is available for getCurrentDeviceDataSource()
    val isDataSourceApiAvailable = Build.VERSION.SDK_INT >= Build.VERSION_CODES.U &&
            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.U) >= 11

    try {
        val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)

        // 2. Safely fetch the package name only if API is available and data exists
        val currentDeviceSpn = if (isDataSourceApiAvailable) {
            healthConnectManager?.getCurrentDeviceDataSource()?.deviceDataOrigin?.packageName
        } else {
            null
        }

        val dataOriginFilters = mutableSetOf(DataOrigin("android"))

        // 3. Explicit null-safety check using .let
        currentDeviceSpn?.let {
            dataOriginFilters.add(DataOrigin(it))
        }

        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = dataOriginFilters
            )
        )

        val stepCount = response[StepsRecord.COUNT_TOTAL]

    } catch (e: Exception) {
        // Now this catch block only handles actual runtime exceptions, 
        // rather than Errors from missing methods.
    }
}

オンデバイスの歩数カウント

  • センサーの使用状況: ヘルスコネクトは SensorManagerTYPE_STEP_COUNTER センサーを利用します。このセンサーは低消費電力に最適化されているため、バックグラウンドでの継続的な歩数トラッキングに最適です。
  • データの粒度: バッテリーの駆動時間を長くするため、歩数データは通常、バッチ処理され、ヘルスコネクト データベースに 1 分に 1 回以下の頻度で書き込まれます。
  • アトリビューション: この機能で 2026 年 6 月より前に記録されたステップは、DataOriginandroid パッケージ名に帰属します。この日付以降は、デバイス固有の SPN に関連付けられます。オンデバイスの手順のアトリビューションの変更を参照してください。
  • 有効化: デバイス上の歩数カウント メカニズムは、デバイス上の少なくとも 1 つのアプリにヘルスコネクト内で READ_STEPS 権限が付与されている場合にのみ有効になります。

バックグラウンドでの読み取りの例

バックグラウンドでデータを読み取るには、マニフェスト ファイルで次の権限を宣言します。

<application>
  <uses-permission android:name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND" />
...
</application>

次の例は、WorkManager を使用して、特定期間内のユーザーの歩数データをバックグラウンドで読み取る方法を示しています。

class ScheduleWorker(appContext: Context, workerParams: WorkerParameters) :
    CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result {
        val healthConnectClient = HealthConnectClient.getOrCreate(applicationContext)
        // Perform background read logic here
        return Result.success()
    }
}
@OptIn(ExperimentalFeatureAvailabilityApi::class)
fun enqueueBackgroundReadWorker(context: Context, healthConnectClient: HealthConnectClient) {
    if (healthConnectClient
            .features
            .getFeatureStatus(
                HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
            ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE
    ) {

        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
}

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 日間の制限。

制限は、読み取り権限をリクエストすることで解除できます。

過去のデータを読み取るには、ReadRecordsRequestdataOriginFilter パラメータで、パッケージ名を 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 の各メソッドは、スローされる可能性のある例外をリストしますが、一般に次のような処理が必要です。

表 1: ヘルスコネクトの例外と推奨されるベスト プラクティス
例外 説明 推奨されるベスト プラクティス
IllegalStateException 次のいずれかの状況が発生した場合にスローされます。

  • ヘルスコネクト サービスを利用できない。
  • リクエストが有効な構造でない(timeRangeFilterInstant オブジェクトが使用される定期的なバケットでの集計リクエストなど)。

リクエストを処理する前に、入力に関する潜在的な問題に対処します。リクエストで値を直接使用する代わりに、カスタム関数内で変数に値を代入するか、パラメータとして使用することをおすすめします。そうすることで、エラー処理戦略を適用できます。
IOException ディスクのデータの読み取りと書き込みで問題が発生した場合にスローされます。この問題を回避するには、次の方法をお試しください。

  • ユーザー入力をバックアップします。
  • 一括書き込みオペレーション中に発生した問題に対処できるようにします。たとえば、プロセスで問題を越えたことを確認してから、残りの操作を行うようにします。
  • リクエストの問題に対処するために、再試行とバックオフの戦略を適用します。

RemoteException SDK が接続されている基となるサービスでエラーが発生したか、サービスとの通信中にエラーが発生した場合にスローされます。

たとえば、アプリが特定の uid を持つレコードを削除しようとした場合に、基となるサービスでチェックして初めてレコードが存在しないことが検出されると、例外がスローされます。
この問題を回避するには、次の方法をお試しください。

  • アプリのデータストアとヘルスコネクトの間で定期的に同期を実行します。
  • リクエストの問題に対処するために、再試行とバックオフの戦略を適用します。

SecurityException 現在付与されていない権限を必要とするリクエストの場合にスローされます。この問題を回避するには、公開したアプリのヘルスコネクトのデータタイプの使用を宣言していることを確認します。また、ヘルスコネクトの権限は、マニフェスト ファイルアクティビティ内で宣言する必要があります。