このガイドは、Health Connect バージョン 1.1.0-alpha12 に対応しています。
このガイドでは、ヘルスコネクトでデータの書き込みまたは更新を行うプロセスについて説明します。
データ構造を設定する
データを書き込む前に、まずレコードを設定する必要があります。50 を超えるデータ型があり、各データ型にそれぞれの構造があります。使用可能なデータ型について詳しくは、Jetpack リファレンスをご覧ください。
基本のレコード
ヘルスコネクトの Steps データ型には、各読み取りの間にユーザーが歩いた歩数が記録されます。歩数は、健康、フィットネス、ウェルネスのプラットフォームで共通の測定値を表します。
次の例は、歩数データを設定する方法を示しています。
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
測定単位を含むレコード
ヘルスコネクトでは、精度を高めるため、測定単位とともに値を格納できます。その一例が、広範囲を包括的にカバーする Nutrition データ型です。総炭水化物からビタミンまで、さまざまな栄養素項目が対象に含まれます。各データポイントは、食事または食品の一部として摂取された可能性のある栄養素を表します。
Nutrition データ型では、すべての栄養素が Mass
の単位で表現され、energy
は Energy
の単位で表現されます。
次の例は、バナナを食べたユーザーの栄養データを設定する方法を示しています。
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(1))
val banana = NutritionRecord(
name = "banana",
energy = 105.0.kilocalories,
dietaryFiber = 3.1.grams,
potassium = 0.422.grams,
totalCarbohydrate = 27.0.grams,
totalFat = 0.4.grams,
saturatedFat = 0.1.grams,
sodium = 0.001.grams,
sugar = 14.0.grams,
vitaminB6 = 0.0005.grams,
vitaminC = 0.0103.grams,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
device = Device(type = Device.TYPE_PHONE)
)
)
系列データを含むレコード
ヘルスコネクトでは系列データのリストを格納できます。その一例が、読み取り間に検出された一連の心拍数のサンプルをキャプチャする Heart Rate データ型です。
このデータ型では、パラメータ samples
は心拍数サンプルのリストで表されます。各サンプルには、beatsPerMinute
値と time
値が含まれています。
次の例は、心拍数の系列データを設定する方法を示しています。
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
val heartRateRecord = HeartRateRecord(
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
// records 10 arbitrary data, to replace with actual data
samples = List(10) { index ->
HeartRateRecord.Sample(
time = startTime + Duration.ofSeconds(index.toLong()),
beatsPerMinute = 100 + index.toLong(),
)
},
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
))
データを書き込む
ヘルスコネクトの一般的なワークフローとして、データの書き込みがあります。レコードを追加するには、insertRecords
を使用します。
次の例は、歩数を挿入するデータを書き込む方法を示しています。
suspend fun insertSteps(healthConnectClient: HealthConnectClient) {
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
try {
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
healthConnectClient.insertRecords(listOf(stepsRecord))
} catch (e: Exception) {
// Run error handling here
}
}
データを更新する
1 つ以上のレコードを変更する必要がある場合、特にアプリのデータストアをヘルスコネクトのデータと同期する必要がある場合は、データを更新できます。既存のデータを更新するには、レコードの検索に使用される ID に応じて 2 つの方法があります。
メタデータ
データを更新するときに必要になるため、最初に Metadata
クラスを確認することをおすすめします。作成時、ヘルスコネクトの各 Record
には metadata
フィールドがあります。同期に関連するプロパティは次のとおりです。
プロパティ | 説明 |
---|---|
id
|
ヘルスコネクトの各 Record には一意の id 値があります。ヘルスコネクトでは、 新しいレコードを挿入するときに、この値が自動的に設定されます。 |
lastModifiedTime
|
各 Record にはレコードの最終更新日時も記録されます。この値はヘルスコネクトによって自動的に入力されます。 |
clientRecordId
|
各 Record に一意の ID を関連付けて、アプリのデータストアで参照として使用することができます。この値はアプリが指定します。 |
clientRecordVersion
|
レコードに clientRecordId がある場合は、clientRecordVersion を使用して、データをアプリ データストアのバージョンと同期させることができます。この値はアプリが指定します。 |
レコード ID を介して更新する
データを更新するには、まず必要なレコードを用意します。必要に応じてレコードに変更を加えます。次に、updateRecords
を呼び出して変更を行います。
次の例は、データを更新する方法を示しています。この目的のために、各レコードのゾーン オフセット値は PST に調整されます。
suspend fun updateSteps(
healthConnectClient: HealthConnectClient,
prevRecordStartTime: Instant,
prevRecordEndTime: Instant
) {
try {
val request = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = StepsRecord::class, timeRangeFilter = TimeRangeFilter.between(
prevRecordStartTime, prevRecordEndTime
)
)
)
val newStepsRecords = arrayListOf<StepsRecord>()
for (record in request.records) {
// Adjusted both offset values to reflect changes
val sr = StepsRecord(
count = record.count,
startTime = record.startTime,
startZoneOffset = record.startTime.atZone(ZoneId.of("PST")).offset,
endTime = record.endTime,
endZoneOffset = record.endTime.atZone(ZoneId.of("PST")).offset,
metadata = record.metadata
)
newStepsRecords.add(sr)
}
healthConnectClient.updateRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
クライアント レコード ID を介してアップサートする
省略可能なクライアント レコード ID とクライアント レコード バージョンの値を使用している場合は、updateRecords
ではなく insertRecords
を使用することをおすすめします。
insertRecords
関数を使用するとデータをアップサートできます。指定されたクライアント レコード ID のセットに基づくデータがヘルスコネクトに存在する場合、データは上書きされます。存在しない場合は、新しいデータとして書き込まれます。このシナリオは、アプリのデータストアからヘルスコネクトにデータを同期する必要がある場合に便利です。
次の例は、アプリのデータストアから取得されたデータに対してアップサートを実行する方法を示しています。
suspend fun pullStepsFromDatastore() : ArrayList<StepsRecord> {
val appStepsRecords = arrayListOf<StepsRecord>()
// Pull data from app datastore
// ...
// Make changes to data if necessary
// ...
// Store data in appStepsRecords
// ...
var sr = StepsRecord(
metadata = Metadata.autoRecorded(
clientRecordId = "Your client record ID",
device = Device(type = Device.TYPE_WATCH)
),
// Assign more parameters for this record
)
appStepsRecords.add(sr)
// ...
return appStepsRecords
}
suspend fun upsertSteps(
healthConnectClient: HealthConnectClient,
newStepsRecords: ArrayList<StepsRecord>
) {
try {
healthConnectClient.insertRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
その後、これらの関数をメインのスレッドで呼び出すことができます。
upsertSteps(healthConnectClient, pullStepsFromDatastore())
クライアント レコード バージョンの値のチェック
データをアップサートするプロセスにクライアント レコード バージョンが含まれている場合、ヘルスコネクトは clientRecordVersion
値の比較チェックを行います。挿入されるデータのバージョンが既存のデータのバージョンよりも高い場合は、アップサートが行われます。それ以外の場合は、この変更は無視され、値は同じままになります。
データにバージョニングを含めるには、バージョニングのロジックに基づいて Metadata.clientRecordVersion
に Long
値を指定する必要があります。
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 100L,
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
clientRecordId = "Your supplied record ID",
clientRecordVersion = 0L, // Your supplied record version
device = Device(type = Device.TYPE_WATCH)
)
)
Upsert は、データが予期せず上書きされるのを防ぐため、変更が発生しても version
を自動更新しません。そのため、今より大きな値を手動で指定する必要があります。
データの書き込みに関するベスト プラクティス
アプリは、独自のソースのデータのみをヘルスコネクトに書き込む必要があります。
アプリのデータが別のアプリからインポートされたものである場合、そのデータをヘルスコネクトに書き込む役割はインポート元のアプリが担います。
また、境界外のデータや内部システムエラーなどの書き込み例外を処理するロジックを実装することもおすすめします。バックオフと再試行の戦略は、ジョブ スケジューリング メカニズムに適用できます。ヘルスコネクトへの書き込みが最終的に失敗した場合でも、アプリは必要な処理を引き続き行える状態でなければなりません。診断に役立てるため、必ずエラーをログに記録して報告してください。
データをトラッキングする場合、アプリがデータを書き込む方法に応じて、いくつかの推奨事項があります。
パッシブ トラッキング
これには、受動的なフィットネス管理または健康管理を行うアプリ(たとえば、バックグラウンドで歩数や心拍数を継続的に記録するアプリ)が含まれます。
アプリは以下の方式でヘルスコネクトに定期的にデータを書き込む必要があります。
- 同期のたびに新しいデータのみを書き込み、前回の同期の後で変更されたデータのみを更新します。
- 1 回の書き込みリクエストのレコード数を最大 1,000 個にしてリクエストをチャンクします。
WorkManager
を使用して、定期的なバックグラウンド タスクを少なくとも 15 分の期間にスケジュール設定します。デバイスがアイドル状態でバッテリー残量が十分にある場合にのみタスクを実行するよう制限します。
val constraints = Constraints.Builder() .requiresBatteryNotLow() .requiresDeviceIdle(true) .build() val writeDataWork = PeriodicWorkRequestBuilder<WriteDataToHealthConnectWorker>( 15, TimeUnit.MINUTES, 5, TimeUnit.MINUTES ) .setConstraints(constraints) .build()
アクティブ トラッキング
これには、エクササイズや睡眠などのイベントベースのトラッキングを行うアプリや、栄養などの手動のユーザー入力を行うアプリが含まれます。これらのレコードは、アプリがフォアグラウンドにある場合、または 1 日に数回使用されるまれなイベントの場合に作成されます。
イベント期間全体を通じて、アプリでヘルスコネクトの実行が維持されないようにします。
データは、次の 2 つの方法のいずれかを使って、ヘルスコネクトに書き込む必要があります。
- イベントの完了後にデータをヘルスコネクトに同期します。たとえば、トラッキング対象のエクササイズ セッションが終了したときにデータを同期します。
WorkManager
を使用して、1 回限りのタスクのデータを後で同期するようにスケジュール設定します。
書き込みの粒度と頻度に関するベスト プラクティス
ヘルスコネクトにデータを書き込むときには、適切な解像度を使用します。適切な解像度を使用すると、一貫性と正確性のあるデータを維持しながら、ストレージの負荷を軽減できます。データの解決には、次の 2 つが含まれます。
- 書き込みの頻度: アプリが新しいデータをヘルスコネクトにプッシュする頻度。たとえば、15 分ごとに新しいデータを書き込みます。
- 書き込まれるデータの粒度: プッシュされるデータがサンプリングされる頻度。たとえば、心拍数のサンプルを 5 秒ごとに書き込みます。すべてのデータ型に同じサンプルレートが必要なわけではありません。歩数データを 1 秒ごとに更新してもあまり意味はありません。60 秒程度の少ない頻度で十分です。一方で、サンプルレートが高いほど、ユーザーは健康とフィットネスのデータをよりきめ細かく把握できます。サンプルレートの頻度は、詳細とパフォーマンスのバランスをとれるように設定する必要があります。
1 日を通してモニタリングされたデータを書き込む
歩数など、継続的に収集されるデータについては、アプリは 1 日を通して少なくとも 15 分ごとにヘルスコネクトに書き込む必要があります。
データ型 |
単位 |
想定される |
例 |
手順 |
手順 |
1 分ごと |
23:14 - 23:15 - 5 歩 23:16 - 23:17 - 22 歩 23:17 - 23:18 - 8 歩 |
歩数 / 分 |
歩/分 |
1 分ごと |
23:14 ~ 23:15 - 5 spm 23:16 - 23:17 - 22 spm 23:17 ~ 23:18 - 8 spm |
車椅子の車輪を押した回数 |
プッシュ |
1 分ごと |
23:14 ~ 23:15 - 5 回 push 23:16 - 23:17 - 22 プッシュ 23:17 ~ 23:18 - 8 回 push |
ActiveCaloriesBurned |
カロリー |
15分毎 |
23:15 ~ 23:30 - 2 カロリー 23:30 ~ 23:45 - 25 カロリー 23:45 ~ 00:00 - 5 カロリー |
TotalCaloriesBurned |
カロリー |
15分毎 |
23:15 ~ 23:30 - 16 カロリー 23:30 ~ 23:45 - 16 カロリー 23:45 ~ 00:00 - 16 カロリー |
距離 |
km/min |
1 分ごと |
23:14 ~ 23:15 - 0.008 km 23:16 - 23:16 - 0.021 km 23:17 ~ 23:18 - 0.012 km |
ElevationGained |
m |
1 分ごと |
20:36 ~ 20:37 - 3.048 m 20:39 ~ 20:40 - 3.048 m 23:23 ~ 23:24 - 9.144 m |
FloorsClimbed |
階 |
1 分ごと |
23:14 - 23:15 - 5 階 23:16 - 23:16 - 22 階 23:17 - 23:18 - 8 階 |
HeartRate |
bpm |
1 分ごと |
午前 6 時 11 分 - 55 bpm |
HeartRateVariabilityRmssd |
ms |
1 分ごと |
午前 6 時 11 分 - 23 ミリ秒 |
RespiratoryRate |
呼吸/分 |
1 分ごと |
23:14 ~ 23:15 - 1 分あたり 60 回 23:16 - 23:16 - 62 回/分 23:17 ~ 23:18 - 1 分あたり 64 回 |
OxygenSaturation |
% |
1 時間ごと |
6:11 - 95.208% |
セッションを書き込む
データは、ワークアウトまたは睡眠セッションの終了時にヘルスコネクトに書き込む必要があります。
おすすめの方法として、睡眠セッションやエクササイズ セッションは、記録デバイスと適切なメタデータ(RecordingMethod
など)を使用して書き込む必要があります。
少なくとも、以下の「想定」列のガイダンスに沿ってアプリケーションを作成する必要があります。可能な限り、「最適」なガイダンスに従います。
エクササイズ中に記録されたデータ
データ型 |
単位 |
想定される |
今後ともどうぞよろしくお願いいたします。 |
例 |
手順 |
手順 |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 5 歩 23:16 - 23:17 - 22 歩 23:17 - 23:18 - 8 歩 |
歩数 / 分 |
歩/分 |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 35 spm 23:16 ~ 23:17 - 37 spm 23:17 ~ 23:18 - 40 spm |
車椅子の車輪を押した回数 |
プッシュ |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 5 回 push 23:16 - 23:17 - 22 プッシュ 23:17 ~ 23:18 - 8 回 push |
CyclingPedalingCadence |
RPM |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 65 rpm 23:16 ~ 23:17 - 70 rpm 23:17 - 23:18 - 68 rpm |
電源 |
ワット |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 250 ワット 23:16 ~ 23:17 - 255 ワット 23:17 - 23:18 - 245 ワット |
速度 |
km/min |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 0.3 km/分 23:16 - 23:17 - 0.4 km/分 23:17 - 23:18 -0.4 km/分 |
距離 |
km/m |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 0.008 km 23:16 - 23:16 - 0.021 km 23:17 ~ 23:18 - 0.012 km |
ActiveCaloriesBurned |
カロリー |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 20 カロリー 23:16 - 23:17 - 20 カロリー 23:17 - 23:18 - 25 カロリー |
TotalCaloriesBurned |
カロリー |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 36 カロリー 23:16 - 23:17 - 36 カロリー 23:17 - 23:18 - 41 カロリー |
ElevationGained |
m |
1 分ごと |
1 秒ごとに |
20:36 ~ 20:37 - 3.048 m 20:39 ~ 20:40 - 3.048 m 23:23 ~ 23:24 - 9.144 m |
ExerciseRoutes |
lat/lng/alt |
3 ~ 5 秒ごと |
1 秒ごとに |
|
HeartRate |
bpm |
1 分ごと |
1 秒ごとに |
23:14 ~ 23:15 - 150 bpm 23:16 - 23:17 -152 bpm 23:17 - 23:18 - 155 bpm |
睡眠中に記録されたデータ
データ型 |
単位 |
想定されるサンプル |
例 |
睡眠段階 |
ステージ |
睡眠ステージごとの詳細な期間 |
23:46 - 23:50 - 覚醒 23:50 ~ 23:56 - 浅い睡眠 23:56 ~ 00:16 - 深い睡眠 |
RestingHeartRate |
bpm |
1 日あたりの単一の値(朝一番に提供) |
午前 6 時 11 分 - 60 bpm |
OxygenSaturation |
% |
1 日あたりの単一の値(朝一番に提供) |
6:11 - 95.208% |