이전 데이터 사용

History API를 사용하면 앱이 피트니스 스토어에서 일괄 작업을 실행할 수 있습니다. 과거 건강 및 웰니스 데이터 읽기, 삽입, 업데이트, 삭제 History API로 다음 작업을 할 수 있습니다.

  • 다른 기기를 사용하여 삽입하거나 기록한 건강 및 웰니스 데이터를 읽습니다. 있습니다.
  • Google 피트니스로 일괄 데이터 가져오기
  • Google 피트니스에서 데이터를 업데이트합니다.
  • 앱이 이전에 저장한 이전 데이터를 삭제합니다.

세션 메타데이터가 포함된 데이터를 삽입하려면 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()

자바

// 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는 은 하루에 걸은 걸음 수를 나타냅니다. 이 특정 사용 사례의 경우 집계된 데이터 포인트에는 두 가지 이점이 있습니다.

  • 앱과 피트니스 스토어는 더 적은 양의 데이터를 교환합니다.
  • 앱에서 데이터를 수동으로 집계하지 않아도 됩니다.

여러 활동 유형의 데이터 집계

앱은 데이터 요청을 사용하여 다양한 유형의 데이터를 검색할 수 있습니다. 이 다음 예시에서는 DataReadRequest: 다음 시간 내에 수행한 각 활동에 대한 칼로리 소모량 지정할 수 있습니다. 결과 데이터는 다음과 같이 활동당 칼로리와 일치합니다. Google 피트니스 앱에 보고되며 각 활동에는 자체 버킷이 있습니다 칼로리 데이터입니다.

Kotlin

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

자바

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()

자바

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();
}

일일 총 데이터 읽기

또한 Google 피트니스를 사용하면 일일 총 걸음 수에 간단하게 액세스할 수 지정할 수 있습니다. 사용 HistoryClient.readDailyTotal() 드림 메서드를 사용하여 현재 일(기기의 현재 시간대 기준) 예를 들어 TYPE_STEP_COUNT_DELTA 데이터 유형을 이 메서드에 추가하여 일일 합계를 가져옵니다. 학습합니다. 매일 집계하는 즉각적인 데이터 유형을 전달할 수 있습니다. 합계입니다. 지원되는 데이터 유형에 대한 자세한 내용은 DataType.getAggregateType

Google 피트니스는 다음을 구독하기 위해 승인을 필요로 하지 않습니다. HistoryClient.readDailyTotal()의 업데이트 TYPE_STEP_COUNT_DELTA개 이 메서드가 기본 계정을 사용하여 호출되고 scopes를 지정합니다. 이는 걸음 수 데이터를 사용할 수 없을 때 권한 패널을 표시합니다(예: Wear OS 시계 화면).

사용자는 Google 피트니스 앱 전체에서 걸음 수가 일관되게 표시되는 것을 선호하므로 Wear OS 시계 모드에 관한 다양한 수요가 증가하기 때문입니다 일관되고 신뢰할 수 있는 경험을 제공합니다. 걸음 수를 일관되게 유지하려면 앱 또는 시계 화면에서 Google 피트니스 플랫폼의 걸음 수를 설정한 다음 개수 업데이트 onExitAmbient() 시계 화면에서 이 데이터를 사용하는 방법에 관한 자세한 내용은 다음을 참고하세요. 시계 화면 정보 표시Android 시계 화면 샘플 애플리케이션을 참조하세요.

데이터 삽입

이전 데이터를 삽입하려면 먼저 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()

자바

// 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)
    }

자바

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));
}

충돌하는 데이터 포인트 관리

DataPoint 드림 앱의 DataSet에는 startTimeendTime 해당 DataSet 내의 고유 간격(DataPoint 간 중복 없음) 인스턴스

앱이 기존 DataPoint DataPoint 인스턴스에서 새 DataPoint가 삭제됩니다. 새 기존 데이터 포인트와 중복될 수 있는 DataPoint의 경우 HistoryClient.updateData 데이터 업데이트에 설명된 메서드를 사용합니다.

데이터 포인트 기간이 기존 데이터와 겹치는 경우 데이터 포인트를 삽입할 수 없습니다.
포인트

그림 1. insertData() 메서드가 새 데이터 포인트를 처리하는 방법 기존 DataPoint와 충돌합니다.

데이터 업데이트

Google 피트니스를 사용하면 앱에서 이전 건강 및 웰빙 데이터를 업데이트할 수 있습니다 있습니다. 새 DataSet에 대한 이전 데이터를 추가하거나 새로 추가하려면 기존 데이터와 충돌하지 않는 인스턴스 DataPoint개 포인트가 있으면 HistoryApi.insertData 메서드를 사용합니다.

이전 데이터를 업데이트하려면 HistoryClient.updateData 메서드를 사용합니다. 이 메서드는 DataPoint와 겹치는 기존 DataPoint 인스턴스를 삭제합니다. 인스턴스를 추가합니다.

이전 건강 및 웰니스 데이터를 업데이트하려면 먼저 DataSet을(를) 만드세요. 인스턴스:

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()

자바

// 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)
    }

자바

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 피트니스를 사용하면 앱에서 이전 건강 및 웰빙 데이터를 삭제할 수 있습니다 있습니다.

이전 데이터를 삭제하려면 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)
    }

자바

// 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));

앱은 특정 세션 또는 모든 데이터 삭제 자세한 내용은 DataDeleteRequest

데이터 업데이트 등록

앱은 SensorsClient

빈도가 낮고 수동으로 계산하는 다른 유형의 데이터에는 이러한 측정 데이터가 Google 피트니스 데이터베이스를 추적할 수 있습니다 이러한 데이터 유형의 예로는 키, 웨이트 및 웨이트 트레이닝과 같은 운동 자세한 내용은 전체 목록 자세히 알아봅니다. 업데이트를 등록하려면 다음을 사용하세요. 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")
    }

자바

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}")
        }
    }
}

자바

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 파일에서 선언해야 합니다.