L'API History consente alla tua app di eseguire operazioni collettive sul Fitness Store: Lettura, inserimento, aggiornamento ed eliminazione di dati storici su salute e benessere. Utilizza l'API History per effettuare le seguenti operazioni:
- Leggere i dati su salute e benessere inseriti o registrati utilizzando altri app.
- Importa dati in batch in Google Fit.
- Aggiornare i dati in Google Fit.
- Eliminare i dati storici memorizzati in precedenza dalla tua app.
Per inserire dati che contengono metadati di sessione, utilizza il metodo API Sessions.
Lettura di dati
Le seguenti sezioni illustrano come leggere diversi tipi di dati aggregati.
Leggere dati dettagliati e aggregati
Per leggere i dati storici, crea un
DataReadRequest
in esecuzione in un'istanza Compute Engine.
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();
L'esempio precedente utilizza punti dati aggregati, dove ogni DataPoint
rappresenta il numero di passi effettuati in un giorno. Per questo particolare caso d'uso,
i punti dati aggregati presentano due vantaggi:
- La tua app e il negozio per il fitness si scambiano quantità di dati minori.
- La tua app non deve aggregare manualmente i dati.
Dati aggregati per più tipi di attività
La tua app può utilizzare le richieste di dati per recuperare molti tipi diversi di dati. La
l'esempio seguente illustra come creare
DataReadRequest
per ottenere le calorie bruciate per ogni attività svolta all'interno
nell'intervallo di tempo specificato. I dati risultanti corrispondono alle calorie per attività
riportate nell'app Google Fit, in cui ogni attività ha il proprio bucket
di dati sulle calorie.
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();
Dopo aver creato un'istanza DataReadRequest
, utilizza il metodo
HistoryClient.readData()
per leggere in modo asincrono i dati storici.
L'esempio seguente mostra come ottenere le istanze DataPoint
da
un 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(); }
Lettura dei dati totali giornalieri
Google Fit offre anche un semplice accesso al totale giornaliero di
tipo di dati specificato. Utilizza la
HistoryClient.readDailyTotal()
per recuperare il tipo di dati specificato a partire dalla mezzanotte del giorno
giorno nel fuso orario attuale del dispositivo. Ad esempio, trasmetti la
TYPE_STEP_COUNT_DELTA
tipo di dati a questo metodo per recuperare il totale giornaliero
passaggi. Puoi passare in un tipo di dati istantaneo con un'indicazione giornaliera aggregata
totali. Per ulteriori informazioni sui tipi di dati supportati, consulta
DataType.getAggregateType
Google Fit non richiede l'autorizzazione per abbonarsi a
TYPE_STEP_COUNT_DELTA
aggiornamenti da HistoryClient.readDailyTotal()
quando questo metodo viene chiamato utilizzando l'account predefinito e non
vengono specificati gli ambiti.
Questo può essere utile se hai bisogno di dati dei passaggi da utilizzare in aree in cui non è possibile
per mostrare il riquadro delle autorizzazioni, ad esempio sui quadranti Wear OS.
Gli utenti preferiscono vedere un numero di passi coerente nell'app Google Fit,
altre app e i quadranti Wear OS, perché offrono una
coerente e affidabile. Per mantenere coerente il conteggio dei passi, iscriviti a
passi nella piattaforma Google Fit dall'app o dal quadrante e poi
aggiorna il conteggio in
onExitAmbient()
Per ulteriori informazioni su come utilizzare questi dati in un quadrante, vedi
Complicazioni per il quadrante
e l'applicazione di esempio WatchFace per Android.
Inserisci i dati
Per inserire i dati storici, crea prima un'istanza 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();
Dopo aver creato un'istanza DataSet
, utilizza il metodo
HistoryClient.insertData
per aggiungere in modo asincrono questi dati storici.
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)); }
Gestisci i punti dati in conflitto
Ciascuna
DataPoint
nel valore DataSet
della tua app deve avere un valore startTime
e un endTime
che definisce
intervallo univoco all'interno di DataSet
, senza sovrapposizione tra DataPoint
di Compute Engine.
Se la tua app tenta di inserire un nuovo DataPoint
in conflitto con uno esistente
DataPoint
, la nuova istanza DataPoint
viene ignorata. Per inserire un nuovo
DataPoint
che potrebbero sovrapporsi ai punti dati esistenti, utilizza il metodo
HistoryClient.updateData
descritto in Aggiornare i dati.
Figura 1. In che modo il metodo insertData()
gestisce i nuovi punti dati che
è in conflitto con un DataPoint
esistente.
Aggiorna dati
Google Fit consente alla tua app di aggiornare i dati storici relativi a salute e benessere
precedentemente inserite. Per aggiungere dati storici per un nuovo DataSet
o per aggiungerne di nuovi
DataPoint
istanze che non sono in conflitto con i dati esistenti
, utilizza il metodo HistoryApi.insertData
.
Per aggiornare i dati storici, utilizza il metodo HistoryClient.updateData
. Questo
elimina tutte le istanze DataPoint
esistenti che si sovrappongono a DataPoint
aggiunte con questo metodo.
Per aggiornare i dati storici di salute e benessere, devi prima creare un DataSet
istanza:
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();
Dopodiché utilizza DataUpdateRequest.Builder()
per creare una nuova richiesta di aggiornamento dei dati e
utilizza il metodo HistoryClient.updateData
per effettuare la richiesta:
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));
Elimina i dati
Google Fit consente alla tua app di eliminare i dati storici relativi a salute e benessere precedentemente inserite.
Per eliminare i dati storici, utilizza il
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));
Le app possono eliminare i dati da sessioni specifiche
eliminare tutti i dati. Per ulteriori informazioni, consulta il riferimento API per
DataDeleteRequest
Registrati per ricevere aggiornamenti dei dati
La tua app può leggere i dati non elaborati dei sensori in tempo reale registrandoti con
SensorsClient
Per altri tipi di dati che sono meno frequenti e vengono conteggiati manualmente,
l'app può registrarsi per ricevere aggiornamenti quando queste misurazioni vengono inserite
il database di Google Fit. Esempi di questi tipi di dati includono altezza,
il peso e allenamenti come il sollevamento pesi; per ulteriori dettagli, consulta l'elenco completo
dei tipi di dati supportati.
Per registrarti agli aggiornamenti, usa
HistoryClient.registerDataUpdateListener
Il seguente snippet di codice consente a un'app di ricevere una notifica quando l'utente inserisce un nuovo valore per la ponderazione:
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"));
È possibile utilizzare IntentService
per ricevere notifiche di aggiornamenti:
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}"); } } }
IntentService
deve essere dichiarato nel file AndroidManifest.xml
.