L'API History consente alla tua app di eseguire operazioni collettive sul fitness store: lettura, inserimento, aggiornamento ed eliminazione dei dati storici su salute e benessere. Utilizza l'API History per:
- Leggere i dati su salute e benessere inseriti o registrati utilizzando altre app.
- Importare i dati del batch in Google Fit.
- Aggiornare i dati in Google Fit.
- Elimina i dati storici memorizzati in precedenza dalla tua app.
Per inserire dati contenenti metadati di sessione, utilizza l'API Sessions.
Lettura di dati
Le sezioni seguenti spiegano come leggere i diversi tipi di dati aggregati.
Leggere dati dettagliati e aggregati
Per leggere i dati storici, crea un'istanza di 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();
L'esempio precedente utilizza punti dati aggregati, dove ogni DataPoint
rappresenta il numero di passi effettuati in un giorno. In questo caso d'uso particolare,
i punti dati aggregati presentano due vantaggi:
- La tua app e il negozio di fitness scambiano quantità minori di dati.
- La tua app non deve aggregare i dati manualmente.
Aggrega dati per più tipi di attività
La tua app può utilizzare le richieste di dati per recuperare molti tipi diversi di dati. L'esempio seguente mostra come creare un DataReadRequest
per ottenere le calorie bruciate per ogni attività eseguita nell'intervallo di tempo specificato. I dati risultanti corrispondono alle calorie per attività riportate nell'app Google Fit, dove ogni attività riceve 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 di dati totali giornalieri
Google Fit fornisce inoltre un accesso semplice al totale giornaliero di un
tipo di dati specificato. Utilizza il metodo HistoryClient.readDailyTotal()
per recuperare il tipo di dati specificato a partire dalla mezzanotte del giorno corrente nel fuso orario attuale del dispositivo. Ad esempio, passa il tipo di dati TYPE_STEP_COUNT_DELTA
a questo metodo per recuperare i passaggi totali giornalieri. Puoi trasmettere un tipo di dati istantaneo con un totale giornaliero
aggregato. Per maggiori informazioni sui tipi di dati supportati, consulta
DataType.getAggregateType
.
Google Fit non richiede l'autorizzazione per sottoscrivere gli aggiornamenti di TYPE_STEP_COUNT_DELTA
dal metodo HistoryClient.readDailyTotal()
quando questo metodo viene chiamato utilizzando l'account predefinito e non sono specificati ambiti.
Questo può essere utile se ti servono i dati sui passi da usare in aree in cui non riesci a mostrare il riquadro delle autorizzazioni, ad esempio sui quadranti Wear OS.
Gli utenti preferiscono visualizzare un numero di passi coerente nell'app Google Fit, in altre app e nei quadranti Wear OS, perché in questo modo possono usufruire di un'esperienza coerente e affidabile. Per mantenere costante il numero di passi, abbonati ai passaggi nella piattaforma Google Fit dall'app o dal quadrante, poi aggiorna il conteggio in onExitAmbient()
.
Per ulteriori informazioni su come utilizzare questi dati in un quadrante, vedi Complicazioni per il quadrante e Applicazione di esempio Android WatchFace.
Inserisci i dati
Per inserire 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 questi dati storici in modo asincrono.
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 punti dati in conflitto
Ogni elemento DataPoint
nell'elemento DataSet
dell'app deve avere un valore startTime
e un endTime
che definisca un intervallo univoco all'interno di DataSet
, senza sovrapposizioni tra le istanze DataPoint
.
Se la tua app tenta di inserire un nuovo DataPoint
in conflitto con un'istanza DataPoint
esistente, il nuovo DataPoint
viene ignorato. Per inserire un nuovo DataPoint
che potrebbe sovrapporsi a punti dati esistenti, utilizza il metodo HistoryClient.updateData
descritto in Aggiornare i dati.
Figura 1. Il modo in cui il metodo insertData()
gestisce i nuovi punti dati
in conflitto con un DataPoint
esistente.
Aggiorna dati
Google Fit consente alla tua app di aggiornare i dati cronologici su salute e benessere inseriti in precedenza. Per aggiungere dati storici per un nuovo DataSet
o per aggiungere nuove istanze DataPoint
che non sono in conflitto con i punti dati esistenti, utilizza il metodo HistoryApi.insertData
.
Per aggiornare i dati storici, utilizza il metodo HistoryClient.updateData
. Questo metodo elimina tutte le istanze DataPoint
esistenti che si sovrappongono a quelle DataPoint
aggiunte utilizzando questo metodo.
Per aggiornare i dati storici su salute e benessere, crea prima un'istanza 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()
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();
Quindi, 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 cronologici su salute e benessere inseriti in precedenza.
Per eliminare i dati storici, utilizza il metodo 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 di sessioni specifiche o
tutti i dati. Per maggiori informazioni, consulta il riferimento API per DataDeleteRequest
.
Registrati per gli aggiornamenti dei dati
L'app può leggere i dati non elaborati dei sensori in tempo reale registrandosi con
SensorsClient
.
Per altri tipi di dati meno frequenti e conteggiati manualmente, la tua
app può registrarsi per ricevere aggiornamenti quando queste misurazioni vengono inserite nel
database di Google Fit. Esempi di questi tipi di dati includono altezza, peso ed esercizi come il sollevamento pesi; per ulteriori dettagli, consulta l'elenco completo dei tipi di dati supportati.
Per registrarti per gli aggiornamenti, utilizza
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 un 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
.