過去のデータを操作する

History API を使用すると、アプリはフィットネス ストアに対して一括操作を実行できます。 健康とウェルネスに関する履歴データの読み取り、挿入、更新、削除 History API を使用して、次のことを行います。

  • 他の方法を使って挿入または記録された健康とウェルネスに関するデータを読み取る 。
  • バッチデータを Google Fit にインポートします。
  • Google Fit のデータを更新します。
  • アプリが以前に保存した履歴データを削除します。

セッション メタデータを含むデータを挿入するには、 Sessions API

データを読み取る

以降のセクションでは、さまざまな種類の集計データを読み取る方法について説明します。

詳細データと集計データを読み取る

過去のデータを読み取るには、 DataReadRequest 構成されます

Kotlin

// Read the data that's been collected throughout the past week.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusWeeks(1)
Log.i(TAG, "Range Start: $startTime")
Log.i(TAG, "Range End: $endTime")

val readRequest =
    DataReadRequest.Builder()
        // The data request can specify multiple data types to return,
        // effectively combining multiple data queries into one call.
        // This example demonstrates aggregating only one data type.
        .aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
        // Analogous to a "Group By" in SQL, defines how data should be
        // aggregated.
        // bucketByTime allows for a time span, whereas bucketBySession allows
        // bucketing by <a href="/fit/android/using-sessions">sessions</a>.
        .bucketByTime(1, TimeUnit.DAYS)
        .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build()

Java

// Read the data that's been collected throughout the past week.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusWeeks(1);
Log.i(TAG, "Range Start: $startTime");
Log.i(TAG, "Range End: $endTime");

DataReadRequest readRequest = new DataReadRequest.Builder()
        // The data request can specify multiple data types to return,
        // effectively combining multiple data queries into one call.
        // This example demonstrates aggregating only one data type.
        .aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
        // Analogous to a "Group By" in SQL, defines how data should be
        // aggregated.
        // bucketByTime allows for a time span, while bucketBySession allows
        // bucketing by sessions.
        .bucketByTime(1, TimeUnit.DAYS)
        .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

前の例では、集計されたデータポイントを使用しています。各 DataPoint 1 日の歩数を表します。この特定のユースケースでは 2 つの利点があります。

  • アプリとフィットネス ストアが交換するデータの量は少なくなっています。
  • アプリでデータを手動で集計する必要はありません。

複数のアクティビティ タイプのデータを集計する

アプリでは、データ リクエストを使用してさまざまな種類のデータを取得できます。「 次の例は、BigQuery の DataReadRequest の範囲内で行った各アクティビティの消費カロリーを取得 表示されます。得られたデータは、1 活動あたりのカロリーと次のように一致します。 各アクティビティに個別のバケットが割り当てられる カロリーデータを抽出できます。

Kotlin

val readRequest = DataReadRequest.Builder()
    .aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
    .bucketByActivityType(1, TimeUnit.SECONDS)
    .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .build()

Java

DataReadRequest readRequest = new DataReadRequest.Builder()
        .aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
        .bucketByActivityType(1, TimeUnit.SECONDS)
        .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

DataReadRequest インスタンスを作成したら、 HistoryClient.readData() メソッドを使用し、過去のデータを非同期で読み取ることができます。

次の例は、DataPoint インスタンスを取得する方法を示しています。 DataSet:

Kotlin

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .readData(readRequest)
    .addOnSuccessListener { response ->
        // The aggregate query puts datasets into buckets, so flatten into a
        // single list of datasets
        for (dataSet in response.buckets.flatMap { it.dataSets }) {
            dumpDataSet(dataSet)
        }
    }
    .addOnFailureListener { e ->
        Log.w(TAG,"There was an error reading data from Google Fit", e)
    }

fun dumpDataSet(dataSet: DataSet) {
    Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}")
    for (dp in dataSet.dataPoints) {
        Log.i(TAG,"Data point:")
        Log.i(TAG,"\tType: ${dp.dataType.name}")
        Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
        Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
        for (field in dp.dataType.fields) {
            Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
        }
    }
}

fun DataPoint.getStartTimeString() = Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime().toString()

fun DataPoint.getEndTimeString() = Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime().toString()

Java

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .readData(readRequest)
        .addOnSuccessListener (response -> {
            // The aggregate query puts datasets into buckets, so convert to a
            // single list of datasets
            for (Bucket bucket : response.getBuckets()) {
                for (DataSet dataSet : bucket.getDataSets()) {
                    dumpDataSet(dataSet);
                }
            }
        })
        .addOnFailureListener(e ->
            Log.w(TAG, "There was an error reading data from Google Fit", e));

}

private void dumpDataSet(DataSet dataSet) {
    Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}");
    for (DataPoint dp : dataSet.getDataPoints()) {
        Log.i(TAG,"Data point:");
        Log.i(TAG,"\tType: ${dp.dataType.name}");
        Log.i(TAG,"\tStart: ${dp.getStartTimeString()}");
        Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}");
        for (Field field : dp.getDataType().getFields()) {
            Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}");
        }
    }
}


private String getStartTimeString() {
    return Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
        .atZone(ZoneId.systemDefault())
        .toLocalDateTime().toString();
}

private String getEndTimeString() {
    return Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime().toString();
}

1 日の合計データを読み取る

Google Fit では、1 日あたりの合計日数も簡単に データタイプを指定します。こちらの HistoryClient.readDailyTotal() メソッドを使用し、現在の時刻の午前 0 時時点で指定したデータ型 日付(デバイスの現在のタイムゾーン)たとえば、 1 日の合計を取得するには、TYPE_STEP_COUNT_DELTA データ型をこのメソッドに追加します。 できます。日別の集計がある瞬間的なデータ型を渡すことができます。 確認できます。サポートされるデータ型の詳細については、このモジュールの DataType.getAggregateType

Google Fit に登録するための認証は必要ありません HistoryClient.readDailyTotal() からの最新情報 TYPE_STEP_COUNT_DELTA 件 メソッドがデフォルトのアカウントを使用して呼び出され、 scopes が指定されている。 これは、走行できない地域で歩数データを必要とする場合に便利です。 Wear OS のウォッチフェイスなどに権限パネルを表示します。

ユーザーは Google Fit アプリで一貫した歩数を好みます。 その他のアプリ、Wear OS ウォッチフェイスなどです。これにより、 一貫した信頼性の高いエクスペリエンスを提供できます。歩数を一定に保つには、 アプリまたはウォッチフェイスから Google Fit プラットフォームで行った手順 新しい Pod の数を onExitAmbient()。 このデータをウォッチフェイスで使用する方法の詳細については、以下をご覧ください。 ウォッチフェイスの追加機能 Android WatchFace サンプルアプリ

データの挿入

過去のデータを挿入するには、まず DataSet インスタンスを作成します。

Kotlin

// Declare that the data being inserted was collected during the past hour.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusHours(1)

// Create a data source
val dataSource = DataSource.Builder()
    .setAppPackageName(this)
    .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
    .setStreamName("$TAG - step count")
    .setType(DataSource.TYPE_RAW)
    .build()

// For each data point, specify a start time, end time, and the
// data value -- in this case, 950 new steps.
val stepCountDelta = 950
val dataPoint =
    DataPoint.builder(dataSource)
        .setField(Field.FIELD_STEPS, stepCountDelta)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build()

val dataSet = DataSet.builder(dataSource)
    .add(dataPoint)
    .build()

Java

// Declare that the data being inserted was collected during the past hour.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusHours(1);

// Create a data source
DataSource dataSource = new DataSource.Builder()
        .setAppPackageName(this)
        .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .setStreamName("$TAG - step count")
        .setType(DataSource.TYPE_RAW)
        .build();

// For each data point, specify a start time, end time, and the
// data value -- in this case, 950 new steps.
int stepCountDelta = 950;
DataPoint dataPoint = DataPoint.builder(dataSource)
        .setField(Field.FIELD_STEPS, stepCountDelta)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

DataSet dataSet = DataSet.builder(dataSource)
        .add(dataPoint)
        .build();

DataSet インスタンスを作成したら、 HistoryClient.insertData メソッドを使用すると、この履歴データを非同期で追加できます。

Kotlin

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .insertData(dataSet)
    .addOnSuccessListener {
        Log.i(TAG, "DataSet added successfully!")
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "There was an error adding the DataSet", e)
    }

Java

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .insertData(dataSet)
        .addOnSuccessListener (unused ->
                Log.i(TAG, "DataSet added successfully!"))
        .addOnFailureListener(e ->
                Log.w(TAG, "There was an error adding the DataSet", e));
}

競合するデータポイントを管理する

DataPointDataSet として、startTimeendTime その DataSet 内のユニーク間隔、DataPoint の間に重複なし 説明します。

アプリが既存のバージョンと競合する新しい DataPoint を挿入しようとした場合 DataPoint インスタンスの場合、新しい DataPoint は破棄されます。新しい DataPoint が既存のデータポイントと重複する可能性がある場合は、 HistoryClient.updateData メソッド(データを更新するを参照)

期間が既存のデータと重複する場合、データポイントは挿入できません
ポイント

図 1. insertData() メソッドによる新しいデータポイントの処理方法 既存の DataPoint と競合します。

データの更新

Google Fit では、健康とウェルネスに関する過去のデータをアプリで更新できます。 挿入しました。新しいDataSetの過去のデータを追加したり、新しい 既存のデータと競合しない DataPoint 個のインスタンス ポイントを確認するには、HistoryApi.insertData メソッドを使用します。

過去のデータを更新するには、HistoryClient.updateData メソッドを使用します。この メソッドは、DataPoint と重複する既存の DataPoint インスタンスを削除します。 追加されているインスタンスの数を表します。

健康とウェルネスに関する過去のデータを更新するには、まず DataSet を作成します instance:

Kotlin

// Declare that the historical data was collected during the past 50 minutes.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusMinutes(50)

// Create a data source
val dataSource  = DataSource.Builder()
    .setAppPackageName(this)
    .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
    .setStreamName("$TAG - step count")
    .setType(DataSource.TYPE_RAW)
    .build()

// Create a data set
// For each data point, specify a start time, end time, and the
// data value -- in this case, 1000 new steps.
val stepCountDelta = 1000

val dataPoint = DataPoint.builder(dataSource)
    .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .setField(Field.FIELD_STEPS, stepCountDelta)
    .build()

val dataSet = DataSet.builder(dataSource)
    .add(dataPoint)
    .build()

Java

// Declare that the historical data was collected during the past 50 minutes.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusMinutes(50);

// Create a data source
DataSource dataSource = new DataSource.Builder()
        .setAppPackageName(this)
        .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .setStreamName("$TAG - step count")
        .setType(DataSource.TYPE_RAW)
        .build();

// Create a data set
// For each data point, specify a start time, end time, and the
// data value -- in this case, 1000 new steps.
int stepCountDelta = 1000;

DataPoint dataPoint = DataPoint.builder(dataSource)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .setField(Field.FIELD_STEPS, stepCountDelta)
        .build();

DataSet dataSet = DataSet.builder(dataSource)
        .add(dataPoint)
        .build();

次に、DataUpdateRequest.Builder() を使用して新しいデータ更新リクエストを作成します。 HistoryClient.updateData メソッドを使用してリクエストを行います。

Kotlin

val request = DataUpdateRequest.Builder()
    .setDataSet(dataSet)
    .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .build()

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .updateData(request)
    .addOnSuccessListener {
        Log.i(TAG, "DataSet updated successfully!")
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "There was an error updating the DataSet", e)
    }

Java

DataUpdateRequest request = new DataUpdateRequest.Builder()
        .setDataSet(dataSet)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .updateData(request)
        .addOnSuccessListener(unused ->
                Log.i(TAG, "DataSet updated successfully!"))
        .addOnFailureListener(e ->
                Log.w(TAG, "There was an error updating the DataSet", e));

データの削除

Google Fit では、健康とウェルネスに関する過去のデータをアプリから削除できます 挿入しました。

過去のデータを削除するには、 HistoryClient.deleteData メソッド:

Kotlin

// Declare that this code deletes step count information that was collected
// throughout the past day.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusDays(1)

// Create a delete request object, providing a data type and a time interval
val request = DataDeleteRequest.Builder()
    .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
    .build()

// Invoke the History API with the HistoryClient object and delete request, and
// then specify a callback that will check the result.
Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .deleteData(request)
    .addOnSuccessListener {
        Log.i(TAG, "Data deleted successfully!")
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "There was an error with the deletion request", e)
    }

Java

// Declare that this code deletes step count information that was collected
// throughout the past day.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusDays(1);

// Create a delete request object, providing a data type and a time interval
DataDeleteRequest request = new DataDeleteRequest.Builder()
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .build();

// Invoke the History API with the HistoryClient object and delete request, and
// then specify a callback that will check the result.
Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .deleteData(request)
        .addOnSuccessListener (unused ->
                Log.i(TAG, "Data deleted successfully!"))
        .addOnFailureListener(e ->
        Log.w(TAG, "There was an error with the deletion request", e));

アプリは、特定のセッションまたは すべてのデータを削除できます。詳細は API リファレンスを DataDeleteRequest

データ更新に登録する

登録することで、アプリで生のセンサーデータをリアルタイムで読み取ることができます。 SensorsClient

その他の種類が少なく、手動でカウントされるデータについては、 アプリは、これらの測定値が挿入されたときに更新を受信するよう登録できる Google Fit データベースたとえば、身長、 ウェイトリフティングなどのワークアウト詳しくは、こちらのリスト サポートされています。 最新情報を登録するには、次のコマンドを使用します。 HistoryClient.registerDataUpdateListener

次のコード スニペットを使用すると、ユーザーが新しいスペースを入力すると、アプリに通知が送信されます。 値を返します。

Kotlin

val intent = Intent(this, MyDataUpdateService::class.java)
val pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

val request = DataUpdateListenerRegistrationRequest.Builder()
    .setDataType(DataType.TYPE_WEIGHT)
    .setPendingIntent(pendingIntent)
    .build()

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .registerDataUpdateListener(request)
    .addOnSuccessListener {
        Log.i(TAG, "DataUpdateListener registered")
    }

Java

Intent intent = new Intent(this, MyDataUpdateService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

DataUpdateListenerRegistrationRequest request = new DataUpdateListenerRegistrationRequest.Builder()
        .setDataType(DataType.TYPE_WEIGHT)
        .setPendingIntent(pendingIntent)
        .build();

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .registerDataUpdateListener(request)
        .addOnSuccessListener(unused ->
                Log.i(TAG, "DataUpdateListener registered"));

IntentService を使用して、更新の通知を受け取ることができます。

Kotlin

class MyDataUpdateService : IntentService("MyDataUpdateService") {
    override fun onHandleIntent(intent: Intent?) {
        val update = DataUpdateNotification.getDataUpdateNotification(intent)
        // Show the time interval over which the data points were collected.
        // To extract specific data values, in this case the user's weight,
        // use DataReadRequest.
        update?.apply {
            val start = getUpdateStartTime(TimeUnit.MILLISECONDS)
            val end = getUpdateEndTime(TimeUnit.MILLISECONDS)

            Log.i(TAG, "Data Update start: $start end: $end DataType: ${dataType.name}")
        }
    }
}

Java

public class MyDataUpdateService extends IntentService {

    public MyDataUpdateService(String name) {
        super("MyDataUpdateService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (intent != null) {
            DataUpdateNotification update = DataUpdateNotification.getDataUpdateNotification(intent);

            // Show the time interval over which the data points
            // were collected.
            // To extract specific data values, in this case the user's weight,
            // use DataReadRequest.
            if (update != null) {
                long start = update.getUpdateStartTime(TimeUnit.MILLISECONDS);
                long end = update.getUpdateEndTime(TimeUnit.MILLISECONDS);
            }

            Log.i(TAG, "Data Update start: $start end: $end DataType: ${dataType.name}");
        }
    }
}

IntentServiceAndroidManifest.xml ファイルで宣言する必要があります。