Questa guida è compatibile con la versione 1.1.0-alpha12 di Connessione Salute.
Questa guida illustra la procedura di scrittura o aggiornamento dei dati in Connessione Salute.
Gestire i valori zero
Alcuni tipi di dati come passi, distanza o calorie potrebbero avere un valore di 0
.
Scrivi solo valori pari a zero quando riflettono una vera inattività mentre l'utente indossava
il dispositivo. Non scrivere valori pari a zero se il dispositivo non è stato indossato, se mancano dati
o se la batteria è scarica. In questi casi, ometti il record per evitare dati fuorvianti.
Configurare la struttura dei dati
Prima di scrivere i dati, dobbiamo configurare i record. Per più di 50 tipi di dati, ognuno con le rispettive strutture. Per ulteriori dettagli sui tipi di dati disponibili, consulta le Informazioni di riferimento su Jetpack.
Record di base
Il tipo di dati Passi in Connessione Salute acquisisce il numero di passi effettuati da un utente tra una lettura e l'altra. Il conteggio dei passi rappresenta una misurazione comune su piattaforme di salute, fitness e benessere.
Il seguente esempio mostra come impostare i dati del conteggio dei passi:
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)
)
)
Record con unità di misura
Connessione Salute può memorizzare i valori insieme alle relative unità di misura per fornire accuratezza. Un esempio è il tipo di dati Nutrizione, che è vasto e completo. Include un'ampia varietà di campi facoltativi per i nutrienti, che vanno dai carboidrati totali alle vitamine. Ogni punto dati rappresenta i nutrienti che sono stati potenzialmente consumati come parte di un pasto o di un alimento.
In questo tipo di dati, tutti i nutrienti sono rappresentati in unità di
Mass
, mentre energy
è rappresentato in unità di Energy
.
L'esempio seguente mostra come impostare i dati nutrizionali per un utente che ha mangiato una banana:
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)
)
)
Record con dati di serie
Connessione Salute può memorizzare un elenco di dati delle serie. Un esempio è il tipo di dati Battito cardiaco, che acquisisce una serie di campioni di battiti rilevati tra le letture.
In questo tipo di dati, il parametro samples
è rappresentato da un elenco di
campioni di battito cardiaco. Ogni campione contiene un valore beatsPerMinute
e un valore time
.
Il seguente esempio mostra come impostare i dati delle serie di battito cardiaco:
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)
))
Richiedere le autorizzazioni all'utente
Dopo aver creato un'istanza client, l'app deve richiedere le autorizzazioni all'utente. Gli utenti devono poter concedere o negare le autorizzazioni in qualsiasi momento.
A questo scopo, crea un insieme di autorizzazioni per i tipi di dati richiesti. Assicurati che le autorizzazioni nel set siano dichiarate prima nel file manifest di Android.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
HealthPermission.getReadPermission(HeartRateRecord::class),
HealthPermission.getWritePermission(HeartRateRecord::class),
HealthPermission.getReadPermission(StepsRecord::class),
HealthPermission.getWritePermission(StepsRecord::class)
)
Utilizza getGrantedPermissions
per verificare se alla tua app sono già state concesse le autorizzazioni richieste. In caso contrario, utilizza
createRequestPermissionResultContract
per richiedere
queste autorizzazioni. Viene visualizzata la schermata delle autorizzazioni di Connessione Salute.
// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
if (granted.containsAll(PERMISSIONS)) {
// Permissions successfully granted
} else {
// Lack of required permissions
}
}
suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
val granted = healthConnectClient.permissionController.getGrantedPermissions()
if (granted.containsAll(PERMISSIONS)) {
// Permissions already granted; proceed with inserting or reading data
} else {
requestPermissions.launch(PERMISSIONS)
}
}
Poiché gli utenti possono concedere o revocare le autorizzazioni in qualsiasi momento, la tua app deve controllare periodicamente le autorizzazioni concesse e gestire gli scenari in cui l'autorizzazione viene persa.
Scrittura di dati
Uno dei flussi di lavoro comuni in Connessione Salute è la scrittura dei dati. Per aggiungere record,
utilizza insertRecords
.
Il seguente esempio mostra come scrivere i conteggi dei passi di inserimento dei dati:
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
}
}
Aggiorna dati
Se devi modificare uno o più record, soprattutto quando devi sincronizzare l'archivio dati dell'app con i dati di Connessione Salute, puoi aggiornare i tuoi dati. Esistono due modi per aggiornare i dati esistenti, a seconda dell'identificatore utilizzato per trovare i record.
Metadati
Vale la pena esaminare prima la classe Metadata
, poiché è necessaria quando
si aggiornano i dati. Al momento della creazione, ogni Record
in Connessione Salute ha un campo
metadata
. Le seguenti proprietà sono pertinenti per la sincronizzazione:
Proprietà | Descrizione |
---|---|
id
|
Ogni Record in Health Connect ha un valore id
univoco.Connessione Salute compila automaticamente questo campo quando inserisci un nuovo record. |
lastModifiedTime
|
Ogni Record tiene traccia anche dell'ultima modifica del record.Connessione Salute compila automaticamente questo campo. |
clientRecordId
|
Ogni Record può avere un ID univoco associato
che funge da riferimento nel datastore dell'app.
La tua app fornisce questo valore. |
clientRecordVersion
|
Se un record contiene clientRecordId , clientRecordVersion può essere utilizzato per consentire la sincronizzazione dei dati con la versione nel datastore dell'app.La tua app fornisce questo valore. |
Aggiornamento dopo la lettura per intervallo di tempo
Per aggiornare i dati, prepara prima i record necessari. Apporta le modifiche necessarie ai record. Poi chiama il numero updateRecords
per apportare
le modifiche.
Il seguente esempio mostra come aggiornare i dati. A questo scopo, i valori di offset del fuso orario di ogni record vengono modificati in 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
}
}
Upsert tramite ID record cliente
Se utilizzi i valori facoltativi ID record cliente e Versione record cliente,
ti consigliamo di utilizzare insertRecords
anziché updateRecords
.
La funzione insertRecords
è in grado di eseguire l'upsert dei dati.
Se i dati esistono in Connessione Salute in base al set specificato di ID record client, vengono sovrascritti. In caso contrario, viene scritto come nuovi dati.
Questo scenario è utile ogni volta che devi sincronizzare i dati
dal datastore dell'app a Connessione Salute.
Il seguente esempio mostra come eseguire un'operazione di upsert sui dati estratti dall'archivio dati dell'app:
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
}
}
Dopodiché, puoi chiamare queste funzioni nel thread principale.
upsertSteps(healthConnectClient, pullStepsFromDatastore())
Controllo del valore nella versione del record cliente
Se la procedura di upsert dei dati include la versione del record cliente, Connessione Salute esegue controlli di confronto nei valori clientRecordVersion
. Se la versione dei dati inseriti è superiore a quella dei dati esistenti, viene eseguito l'upsert. In caso contrario, la procedura
ignora la modifica e il valore rimane invariato.
Per includere il controllo delle versioni nei dati, devi fornire
Metadata.clientRecordVersion
con un valore Long
in base alla logica
di controllo delle versioni.
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)
)
)
Gli upsert non incrementano automaticamente version
ogni volta che vengono apportate modifiche,
impedendo qualsiasi istanza imprevista di sovrascrittura dei dati. In questo caso, devi
fornire manualmente un valore più alto.
Best practice per la scrittura dei dati
Le app devono scrivere in Connessione Salute solo dati di propria origine.
Se i dati della tua app sono stati importati da un'altra app, la responsabilità di scrivere i propri dati in Connessione Salute ricade sull'altra app.
È anche consigliabile implementare una logica che gestisca le eccezioni di scrittura, ad esempio i dati al di fuori dei limiti o un errore del sistema interno. Puoi applicare le tue strategie di backoff e ripetizione a un meccanismo di pianificazione dei job. Se la scrittura in Connessione Salute non va a buon fine, assicurati che la tua app possa superare questo punto di esportazione. Non dimenticare di registrare e segnalare gli errori per facilitare la diagnosi.
Quando monitori i dati, puoi seguire alcuni suggerimenti a seconda del modo in cui la tua app scrive i dati.
Gestione del fuso orario
Quando scrivi record basati sul tempo, evita di impostare offset su zoneOffset.UTC
per impostazione predefinita, perché ciò può comportare timestamp imprecisi quando gli utenti si trovano in altre
zone. Calcola invece l'offset in base alla posizione effettiva del dispositivo.
Puoi recuperare il fuso orario del dispositivo utilizzando ZoneId.systemDefault()
.
val endTime = Instant.now()
val startTime = endTime.minus(java.time.Duration.ofDays(1))
val stepsRecords = mutableListOf<StepsRecord>()
var sampleTime = startTime
val minutesBetweenSamples = 15L
while (sampleTime < endTime) {
// Get the default ZoneId then convert it to an offset
val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(sampleTime)
stepsRecords += StepsRecord(
startTime = sampleTime.minus(java.time.Duration.ofMinutes(minutesBetweenSamples)),
startZoneOffset = zoneOffset,
endTime = sampleTime,
endZoneOffset = zoneOffset,
count = Random.nextLong(1, 100),
metadata = Metadata.unknownRecordingMethod(),
)
sampleTime = sampleTime.plus(java.time.Duration.ofMinutes(minutesBetweenSamples))
}
healthConnectClient.insertRecords(
stepsRecords
)
Per ulteriori dettagli, consulta la documentazione relativa a ZoneId
.
Monitoraggio passivo
Sono incluse le app che eseguono il monitoraggio passivo di fitness o salute, ad esempio la registrazione continua in background di passi o frequenza cardiaca.
La tua app deve scrivere periodicamente i dati in Connessione Salute nei seguenti modi:
- A ogni sincronizzazione, vengono scritti solo i dati nuovi e quelli aggiornati modificati dall'ultima sincronizzazione.
- Dividi le richieste in blocchi di massimo 1000 record per richiesta di scrittura.
- Utilizza
WorkManager
per pianificare attività periodiche in background, con un periodo di tempo di almeno 15 minuti. - Limita l'esecuzione delle attività solo quando il dispositivo è inattivo e la batteria non è in esaurimento.
val constraints = Constraints.Builder()
.requiresBatteryNotLow()
.requiresDeviceIdle(true)
.build()
val writeDataWork = PeriodicWorkRequestBuilder<WriteDataToHealthConnectWorker>(
15,
TimeUnit.MINUTES,
5,
TimeUnit.MINUTES
)
.setConstraints(constraints)
.build()
Monitoraggio attivo
Sono incluse le app che eseguono il monitoraggio basato su eventi come allenamento e sonno o l'inserimento manuale di dati da parte dell'utente, ad esempio l'alimentazione. Questi record vengono creati quando l'app è in primo piano o in rari casi in cui viene utilizzata più volte al giorno.
Assicurati che la tua app non mantenga Connessione Salute in esecuzione per l'intera durata dell'evento.
I dati devono essere scritti in Connessione Salute in uno dei due modi seguenti:
- Sincronizza i dati in Connessione Salute al termine dell'evento. Ad esempio, sincronizza i dati quando l'utente termina una sessione di allenamento monitorata.
- Pianifica un'attività una tantum utilizzando
WorkManager
per sincronizzare i dati in un secondo momento.
Best practice per la granularità e la frequenza delle scritture
Quando scrivi dati in Connessione Salute, utilizza una risoluzione appropriata. L'utilizzo della risoluzione appropriata consente di ridurre il carico di archiviazione, mantenendo comunque dati coerenti e accurati. La risoluzione dei dati comprende due aspetti:
- Frequenza di scrittura: la frequenza con cui la tua applicazione invia nuovi dati a Connessione Salute. Ad esempio, scrivi nuovi dati ogni 15 minuti.
- Granularità dei dati scritti: la frequenza con cui sono stati campionati i dati push. Ad esempio, scrivi campioni di frequenza cardiaca ogni 5 secondi. Non tutti i tipi di dati richiedono la stessa frequenza di campionamento. Non è molto utile aggiornare i dati del conteggio dei passi ogni secondo, rispetto a una cadenza meno frequente, ad esempio ogni 60 secondi. Tuttavia, frequenze di campionamento più elevate possono fornire agli utenti una visione più dettagliata e granulare dei propri dati relativi a salute e attività fisica. Le frequenze di campionamento devono trovare un equilibrio tra dettagli e prestazioni.
Scrivere i dati monitorati durante il giorno
Per i dati raccolti in modo continuativo, come i passi, la tua applicazione deve scrivere in Connessione Salute almeno ogni 15 minuti durante il giorno.
Tipo di dati |
Unità |
Previsto |
Esempio |
Passi |
passi |
Ogni minuto |
23:14 - 23:15 - 5 passi 23:16 - 23:17 - 22 passi 23:17 - 23:18 - 8 passi |
StepsCadence |
passi/min |
Ogni minuto |
23:14 - 23:15 - 5 spm 23:16 - 23:17 - 22 spm 23:17 - 23:18 - 8 spm |
Spinte in sedia a rotelle |
spinte |
Ogni minuto |
23:14 - 23:15 - 5 notifiche push 23:16 - 23:17 - 22 spinte 23:17 - 23:18 - 8 push |
ActiveCaloriesBurned |
Calorie |
Ogni 15 minuti |
23:15 - 23:30 - 2 calorie 23:30 - 23:45 - 25 calorie 23:45 - 00:00 - 5 calorie |
TotalCaloriesBurned |
Calorie |
Ogni 15 minuti |
23:15 - 23:30 - 16 calorie 23:30 - 23:45 - 16 calorie 23:45 - 00:00 - 16 calorie |
Distanza |
km/min |
Ogni minuto |
23:14-23:15 - 0,008 km 23:16 - 23:16 - 0,021 km 23:17 - 23:18 - 0,012 km |
ElevationGained |
m |
Ogni minuto |
20:36 - 20:37 - 3,048 m 20:39 - 20:40 - 3048 m 23:23 - 23:24 - 9,144 m |
PianiSaliti |
piani |
Ogni minuto |
23:14 - 23:15 - 5 piani 23:16 - 23:16 - 22 piani 23:17 - 23:18 - 8 piani |
HeartRate |
bpm |
4 volte al minuto |
6:11:15 - 55 bpm 6:11:30 - 56 bpm 6:11:45 - 56 bpm 6:12:00 - 55 bpm |
HeartRateVariabilityRmssd |
ms |
Ogni minuto |
6:11 - 23 ms |
RespiratoryRate |
respiri/minuto |
Ogni minuto |
23:14 - 23:15 - 60 respiri al minuto 23:16 - 23:16 - 62 respiri/minuto 23:17 - 23:18 - 64 respiri al minuto |
OxygenSaturation |
% |
Ogni ora |
6:11 - 95,208% |
Scrivere sessioni
I dati devono essere scritti in Connessione Salute al termine dell'allenamento o della sessione di sonno.
Come minimo, la tua applicazione deve seguire le indicazioni riportate nella colonna previsto della tabella seguente. Ove possibile, segui le indicazioni migliori.
Sessioni di allenamento e sonno
Il seguente esempio mostra come scrivere i dati durante un allenamento:
Tipo di dati |
Unità |
Previsto |
Cordiali saluti, |
Esempio |
Passi |
passi |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 5 passi 23:16 - 23:17 - 22 passi 23:17 - 23:18 - 8 passi |
StepsCadence |
passi/min |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 35 spm 23:16 - 23:17 - 37 spm 23:17 - 23:18 - 40 spm |
Spinte in sedia a rotelle |
spinte |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 5 notifiche push 23:16 - 23:17 - 22 spinte 23:17 - 23:18 - 8 push |
CyclingPedalingCadence |
rpm / respiri al minuto |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 65 rpm 23:16 - 23:17 - 70 rpm 23:17 - 23:18 - 68 rpm |
Potenza |
watt |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 250 watt 23:16 - 23:17 - 255 watt 23:17 - 23:18 - 245 watt |
Velocità |
km/min |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 0,3 km/min 23:16 - 23:17 - 0,4 km/min 23:17 - 23:18 -0,4 km/min |
Distanza |
km/m |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 0,008 km 23:16 - 23:16 - 0,021 km 23:17 - 23:18 - 0,012 km |
ActiveCaloriesBurned |
Calorie |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 20 calorie 23:16 - 23:17 - 20 calorie 23:17 - 23:18 - 25 calorie |
TotalCaloriesBurned |
Calorie |
Ogni minuto |
Ogni secondo |
23:14-23:15 - 36 calorie 23:16 - 23:17 - 36 calorie 23:17 - 23:18 - 41 calorie |
ElevationGained |
m |
Ogni minuto |
Ogni secondo |
20:36 - 20:37 - 3,048 m 20:39 - 20:40 - 3048 m 23:23 - 23:24 - 9,144 m |
ExerciseRoutes |
lat/lng/alt |
Ogni 3-5 secondi |
Ogni secondo |
|
HeartRate |
bpm |
4 volte al minuto |
Ogni secondo |
23:14-23:15 - 150 bpm |
Il seguente esempio mostra come scrivere i dati durante o dopo una sessione di sonno:
Tipo di dati |
Unità |
Campioni previsti |
Esempio |
Fasi del sonno |
fase |
Periodo di tempo granulare per fase del sonno |
23:46 - 23:50 - sveglio 23:50 - 23:56 - sonno leggero 23:56 - 00:16 - sonno profondo |
RestingHeartRate |
bpm |
Unico valore giornaliero (previsto al mattino presto) |
6:11 - 60 bpm |
OxygenSaturation |
% |
Unico valore giornaliero (previsto al mattino presto) |
6:11 - 95,208% |
Manifestazioni multisportive
Questo approccio utilizza tipi e strutture di dati esistenti e verifica la compatibilità con le implementazioni e i lettori di dati di Connessione Salute attuali. Questo è un approccio comune adottato dalle piattaforme per il fitness.
Il seguente esempio mostra come scrivere i dati per un triathlon:
val swimStartTime = Instant.parse("2024-08-22T08:00:00Z")
val swimEndTime = Instant.parse("2024-08-22T08:30:00Z")
val bikeStartTime = Instant.parse("2024-08-22T08:40:00Z")
val bikeEndTime = Instant.parse("2024-08-22T09:40:00Z")
val runStartTime = Instant.parse("2024-08-22T09:50:00Z")
val runEndTime = Instant.parse("2024-08-22T10:20:00Z")
val swimSession = ExerciseSessionRecord(
startTime = swimStartTime,
endTime = swimEndTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
val bikeSession = ExerciseSessionRecord(
startTime = bikeStartTime,
endTime = bikeEndTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
val runSession = ExerciseSessionRecord(
startTime = runStartTime,
endTime = runEndTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
healthConnectClient.insertRecords(listOf(swimSession, bikeSession, runSession))