Sviluppare esperienze di allenamento con Connessione Salute

Se vuoi creare un'esperienza di allenamento nella tua app, puoi utilizzare Connessione Salute per svolgere attività come:

  • Scrivere le sessioni di allenamento
  • Scrivere i percorsi di allenamento
  • Scrivere le metriche di allenamento, come frequenza cardiaca, velocità e distanza
  • Leggere i dati di allenamento da altre app

Questa guida descrive come creare queste funzionalità di attività fisica, trattando i tipi di dati, l'esecuzione in background, le autorizzazioni, i workflow consigliati e le best practice.

Panoramica: creare un tracker di allenamento completo

Puoi creare un'esperienza di monitoraggio dell'attività fisica completa utilizzando Health Connect seguendo questi passaggi principali:

  • Implementare correttamente le autorizzazioni in base alle autorizzazioni relative alla salute.
  • Registrare le sessioni utilizzando ExerciseSessionRecord.
  • Scrivere i dati di allenamento in modo coerente durante la sessione.
  • Gestire correttamente l'esecuzione in background per verificare l'acquisizione continua dei dati.
  • Leggere i dati della sessione per riepiloghi e analisi post-allenamento.

Questo workflow consente l'interoperabilità con altre app di Health Connect e verifica l'accesso ai dati controllato dall'utente.

Prima di iniziare

Prima di implementare le funzionalità di allenamento:

Concetti principali

Health Connect rappresenta i dati di allenamento utilizzando alcuni componenti principali. Un ExerciseSessionRecord funge da record centrale per un allenamento e contiene dettagli come l'ora di inizio o di fine e il tipo di esercizio. Durante una sessione, è possibile registrare vari tipi di dati, come HeartRateRecord o SpeedRecord. Per le attività all'aperto, ExerciseRoute memorizza i dati GPS, collegati alla sessione corrispondente.

Sessioni di allenamento

ExerciseSessionRecord è il record centrale per i dati di allenamento e rappresenta una singola sessione di allenamento. Ogni record memorizza:

  • startTime
  • endTime
  • exerciseType
  • Metadati della sessione facoltativi (titolo, note)

Un ExerciseSessionRecord può anche contenere percorsi di allenamento, giri e segmenti come parte dei suoi dati. Inoltre, durante una sessione è possibile registrare e associare altri tipi di dati, come HeartRateRecord o SpeedRecord.

Tipi di dati associati

I dati associati alle sessioni di allenamento sono rappresentati da singoli tipi di record. I tipi comuni includono:

Per un elenco completo dei tipi di dati, vedi Tipi di dati di Health Connect.

Percorsi di allenamento

Puoi associare un percorso agli allenamenti all'aperto utilizzando ExerciseRoute. I percorsi sono costituiti da oggetti ExerciseRoute.Location sequenziali, ognuno dei quali contiene:

  • Latitudine e longitudine
  • Altitudine facoltativa
  • Rilevamento facoltativo
  • Informazioni sull'accuratezza
  • Timestamp

Collegare i percorsi delle sessioni

Un ExerciseRoute contiene i dati sulla posizione sequenziali per una sessione di allenamento. Non viene trattato come un record indipendente in Health Connect. Invece, fornisci i dati ExerciseRoute quando inserisci o aggiorni un ExerciseSessionRecord.

Considerazioni sullo sviluppo

Le app di monitoraggio dell'attività fisica spesso devono essere eseguite per periodi prolungati, di frequente in background quando lo schermo è spento. Quando crei le funzionalità per l'attività fisica, è importante considerare come gestire l'esecuzione in background e richiedere le autorizzazioni necessarie per i dati dell'attività fisica.

Esecuzione in background

Le app di allenamento vengono comunemente eseguite con lo schermo spento. In questo stato, devi utilizzare:

  • Servizi in primo piano per il campionamento della posizione e dei sensori
  • WorkManager per la scrittura o la sincronizzazione posticipata
  • Strategie di batch per la scrittura regolare dei record

Mantieni la continuità mantenendo coerente l'ID sessione in tutte le scritture.

Autorizzazioni

La tua app deve richiedere le autorizzazioni di Health Connect pertinenti prima di leggere o scrivere i dati di allenamento. Le autorizzazioni comuni per gli allenamenti includono sessioni di allenamento, percorsi di allenamento e metriche come frequenza cardiaca o velocità. È incluso quanto segue:

  • Sessioni di allenamento: autorizzazioni di lettura e scrittura per ExerciseSessionRecord.
  • Percorsi di allenamento: autorizzazioni di lettura e scrittura per ExerciseRoute.
  • Frequenza cardiaca: autorizzazioni di lettura e scrittura per HeartRateRecord.
  • Velocità: autorizzazioni di lettura e scrittura per SpeedRecord.
  • Distanza: autorizzazioni di lettura e scrittura per DistanceRecord.
  • Calorie: autorizzazioni di lettura e scrittura per TotalCaloriesBurnedRecord.
  • Altitudine raggiunta: autorizzazioni di lettura e scrittura per ElevationGainedRecord.
  • Frequenza del passo: autorizzazioni di lettura e scrittura per StepsCadenceRecord.
  • Potenza: autorizzazioni di lettura e scrittura per PowerRecord.
  • Passi: autorizzazioni di lettura e scrittura per StepsRecord.

Di seguito è riportato un esempio di come richiedere più autorizzazioni per una sessione di attività fisica che include dati su percorso, frequenza cardiaca, distanza, calorie, velocità e passi:

Dopo aver creato un'istanza client, la tua app deve richiedere le autorizzazioni all'utente. Gli utenti devono poter concedere o negare le autorizzazioni in qualsiasi momento.

A questo scopo, crea un set di autorizzazioni per i tipi di dati richiesti. Assicurati che le autorizzazioni nel set siano dichiarate prima nel manifest di Android.

// Create a set of permissions for required data types
val PERMISSIONS =
    setOf(
  HealthPermission.getReadPermission(ExerciseSessionRecord::class),
  HealthPermission.getWritePermission(ExerciseSessionRecord::class),
  HealthPermission.getReadPermission(ExerciseRoute::class),
  HealthPermission.getWritePermission(ExerciseRoute::class),
  HealthPermission.getReadPermission(HeartRateRecord::class),
  HealthPermission.getWritePermission(HeartRateRecord::class),
  HealthPermission.getReadPermission(SpeedRecord::class),
  HealthPermission.getWritePermission(SpeedRecord::class),
  HealthPermission.getReadPermission(DistanceRecord::class),
  HealthPermission.getWritePermission(DistanceRecord::class),
  HealthPermission.getReadPermission(TotalCaloriesBurnedRecord::class),
  HealthPermission.getWritePermission(TotalCaloriesBurnedRecord::class),
  HealthPermission.getReadPermission(StepsRecord::class),
  HealthPermission.getWritePermission(StepsRecord::class)
)

Utilizza getGrantedPermissions per verificare se la tua app ha già le autorizzazioni richieste. In caso contrario, utilizza createRequestPermissionResultContract per richiedere queste autorizzazioni. Viene visualizzata la schermata delle autorizzazioni di Health Connect.

// 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 le autorizzazioni ogni volta prima di utilizzarle e gestire gli scenari in cui l'autorizzazione viene revocata.

Per richiedere le autorizzazioni, chiama la funzione checkPermissionsAndRun:

if (!granted.containsAll(PERMISSIONS)) {
    requestPermissions.launch(PERMISSIONS)
    // Check if required permissions are not granted, and return
  }
// Permissions already granted; proceed with inserting or reading data

Se devi richiedere le autorizzazioni solo per un singolo tipo di dati, ad esempio il battito cardiaco, includi solo quel tipo di dati nel set di autorizzazioni:

L'accesso alla frequenza cardiaca è protetto dalle seguenti autorizzazioni:

  • android.permission.health.READ_HEART_RATE
  • android.permission.health.WRITE_HEART_RATE

Per aggiungere la funzionalità di frequenza cardiaca alla tua app, inizia richiedendo le autorizzazioni per il tipo di dati HeartRateRecord.

Ecco l'autorizzazione che devi dichiarare per poter scrivere la frequenza cardiaca:

<application>
  <uses-permission
android:name="android.permission.health.WRITE_HEART_RATE" />
...
</application>

Per leggere la frequenza cardiaca, devi richiedere le seguenti autorizzazioni:

<application>
  <uses-permission
android:name="android.permission.health.READ_HEART_RATE" />
...
</application>

Implementare una sessione di allenamento

Questa sezione descrive il flusso di lavoro consigliato per la registrazione dei dati di allenamento.

Avviare la sessione

Per creare un nuovo allenamento:

  1. Genera un ID sessione univoco: verifica che questo ID sia stabile. Se il processo dell'app viene interrotto e riavviato, devi essere in grado di riprendere l'utilizzo dello stesso ID per evitare sessioni frammentate.
  2. Imposta un metadata.clientRecordId per evitare duplicati durante i tentativi di sincronizzazione.
  3. Scrivi un ExerciseSessionRecord: includi l'ora di inizio.
  4. Inizia a raccogliere i dati del tipo di dati e del GPS: inizia solo dopo che il record della sessione è stato inizializzato correttamente.

Esempio:

val sessionId = UUID.randomUUID().toString()
val sessionClientId = UUID.randomUUID().toString()

val session = ExerciseSessionRecord(
    id = sessionId,
    exerciseType = ExerciseType.EXERCISE_TYPE_RUNNING,
    startTime = Instant.now(),
    endTime = null,
    metadata = Metadata(clientRecordId = sessionClientId),
)

healthConnectClient.insertRecords(listOf(session))

Registrare i percorsi di allenamento

Per saperne di più sulla lettura delle indicazioni, vedi Leggere i dati non elaborati.

Quando registri un percorso di allenamento, devi raggruppare i dati. Ciò significa che, anziché salvare ogni singolo punto GPS man mano che si verifica, raccogli un gruppo di punti e li salvi tutti in una sola chiamata.

Questo è importante perché ogni volta che la tua app legge o scrive in Health Connect, utilizza una piccola quantità di batteria e potenza di elaborazione.

Il seguente codice mostra come registrare in batch:

// 1. Create a list to hold your route locations
val routeLocations = mutableListOf<ExerciseRoute.Location>()

// 2. Add points to your list as the exercise happens
routeLocations.add(
    ExerciseRoute.Location(
        time = Instant.now(),
        latitude = 37.7749,
        longitude = -122.4194
    )
)

// ... keep adding points over a period of time ...

// 3. Save the whole list at once (Batching)
val session = ExerciseSessionRecord(
    startTime = startTime,
    endTime = endTime,
    exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
    // We pass the whole list here
    exerciseRoute = ExerciseRoute(routeLocations)
)

healthConnectClient.insertRecords(listOf(session))

Terminare una sessione

Dopo aver interrotto la raccolta dei dati:

  • Aggiorna il record: la tua app aggiorna ExerciseSessionRecord con un endTime.
  • Finalizza i dati: facoltativamente, calcola i valori di riepilogo (come la distanza totale o la velocità media) e scrivili come record aggiuntivi.
val finishedSession = session.copy(endTime = Instant.now())
healthConnectClient.updateRecords(listOf(finishedSession))

Leggere i dati di allenamento

Le app possono leggere le sessioni di allenamento e i dati associati per riepilogare l'attività, fornire informazioni sulla salute o sincronizzare i dati con un server esterno. Ad esempio, puoi leggere un ExerciseSessionRecord e poi eseguire una query su HeartRateRecord o DistanceRecord che si sono verificati nello stesso intervallo di tempo.

Se devi sincronizzare i dati di allenamento con un server di backend o mantenere aggiornato il datastore della tua app con Health Connect, utilizza ChangeLogs. In questo modo, puoi recuperare un elenco di record inseriti, aggiornati o eliminati da un punto specifico nel tempo, il che è più efficiente rispetto al monitoraggio manuale delle modifiche o alla lettura ripetuta di tutti i dati. Per saperne di più, vedi Sincronizzare i dati con Health Connect.

Leggere le sessioni

Per leggere le sessioni di allenamento, utilizza un ReadRecordsRequest con ExerciseSessionRecord come tipo. In genere, questo viene filtrato in base a un intervallo di tempo specifico.

suspend fun readExerciseSessions(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    val response = healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = ExerciseSessionRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )

    for (exerciseRecord in response.records) {
        // Process each session
        val exerciseType = exerciseRecord.exerciseType
        val notes = exerciseRecord.notes
    }
}

Leggere i percorsi

Sebbene i dati ExerciseRoute vengano scritti come parte di una sessione di allenamento, devono essere letti separatamente. Utilizza il metodo getExerciseRoute() con l'ID della sessione per leggere i dati del percorso:

suspend fun readExerciseRoute(
    healthConnectClient: HealthConnectClient,
    exerciseSessionRecord: ExerciseSessionRecord
) {
    // Check if the session has a route
    val route = healthConnectClient.getExerciseRoute(
        exerciseSessionRecordId = exerciseSessionRecord.metadata.id
    )

    when (route) {
        is ExerciseRouteResponse.Success -> {
            val locations = route.exerciseRoute.locations
            for (location in locations) {
                // Use latitude, longitude, and altitude
            }
        }
        is ExerciseRouteResponse.NoData -> {
            // Handle case where no route exists
        }
        is ExerciseRouteResponse.ConsentRequired -> {
            // Handle case where permissions are missing
        }
    }
}

Leggere i tipi di dati

Per leggere dati granulari specifici (come la frequenza cardiaca) che si sono verificati durante una sessione, utilizza startTime e endTime della sessione per filtrare la richiesta per quel tipo di dati.

suspend fun readHeartRateData(
    healthConnectClient: HealthConnectClient,
    exerciseSession: ExerciseSessionRecord
) {
    val response = healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = HeartRateRecord::class,
            timeRangeFilter = TimeRangeFilter.between(
                exerciseSession.startTime,
                exerciseSession.endTime
            )
        )
    )

    for (heartRateRecord in response.records) {
        for (sample in heartRateRecord.samples) {
            val bpm = sample.beatsPerMinute
        }
    }
}

Best practice

Segui queste linee guida per migliorare l'affidabilità dei dati e l'esperienza utente:

  • Scrivi spesso durante il monitoraggio attivo: per il monitoraggio attivo, scrivi i dati non appena diventano disponibili o a un intervallo massimo di 15 minuti.
  • Utilizza WorkManager per le sincronizzazioni in background: utilizza WorkManager per le scritture posticipate. Punta a un intervallo di 15 minuti per trovare un equilibrio tra dati in tempo reale ed efficienza della batteria.
  • Richieste di scrittura batch: non scrivere ogni singolo evento del sensore singolarmente. Dividi le richieste in blocchi. Health Connect gestisce fino a 1000 record per richiesta di scrittura.
  • Mantieni gli ID sessione stabili e univoci: utilizza identificatori coerenti per le sessioni. Se una sessione viene modificata o aggiornata, l'utilizzo dello stesso ID impedisce che venga trattata come una nuova sessione separata.
  • Utilizza il batch per i tipi di dati e i punti del percorso: per ridurre il sovraccarico di input/output e preservare la durata della batteria, raggruppa i punti dati in una singola insertRecords anziché scrivere ogni punto singolarmente.
  • Evita di scrivere dati duplicati: utilizza gli ID client: quando crei i record, imposta un metadata.clientRecordId. Health Connect lo utilizza per identificare i record univoci. Se tenti di scrivere un record con un clientRecordId già esistente, Health Connect ignorerà il duplicato o aggiornerà il record esistente anziché crearne uno nuovo. L'impostazione di un metadata.clientRecordId è il modo più efficace per evitare duplicati durante i tentativi di sincronizzazione o le reinstallazioni dell'app.
    val record = StepsRecord(
        count = 100,
        startTime = startTime,
        endTime = endTime,
        startZoneOffset = ZoneOffset.UTC,
        endZoneOffset = ZoneOffset.UTC,
        metadata = Metadata(
            // Use a unique ID from your own database
            clientRecordId = "daily_steps_2023_10_27_user_123"
        )
    )
  • Controlla i dati esistenti: prima della sincronizzazione, esegui una query sull'intervallo di tempo per verificare se esistono già record della tua app.
  • Convalida la precisione del GPS: filtra i campioni GPS a bassa precisione (ad esempio, i punti con un raggio di precisione orizzontale elevato) prima di scrivere in ExerciseRoute per verificare che la mappa sia pulita e professionale.
  • Assicurati che i timestamp non si sovrappongano: verifica che una nuova sessione non inizi prima della fine della precedente. Le sessioni sovrapposte possono causare conflitti nei dashboard di fitness e nei calcoli di riepilogo.
  • Fornisci motivazioni chiare per l'autorizzazione: utilizza il flusso Permission.createIntent per spiegare perché la tua app ha bisogno di accedere ai dati sulla salute, ad esempio: "Per monitorare le tendenze della pressione sanguigna e fornire informazioni".
  • Supporta la pausa e la ripresa: verifica che la tua app gestisca correttamente le pause. Quando un utente mette in pausa, interrompi la raccolta dei punti del percorso e dei tipi di dati in modo che la velocità media e la durata rimangano accurate.
  • Testa le sessioni a lunga esecuzione: monitora il consumo della batteria durante le sessioni che durano diverse ore per verificare che l'intervallo di batch e l'utilizzo dei sensori non scarichino il dispositivo.
  • Allinea i timestamp alle frequenze dei sensori: abbina i timestamp dei record alla frequenza effettiva dei sensori per mantenere un'elevata fedeltà dei dati.

Test

Per verificare la correttezza dei dati e un'esperienza utente di alta qualità, segui queste strategie di test e consulta la documentazione ufficiale Test dei casi d'uso principali.

Strumenti di verifica

  • Health Connect Toolbox: utilizza questa app complementare per esaminare manualmente i record, eliminare i dati di test e simulare le modifiche al database. È il modo migliore per verificare che i record vengano archiviati correttamente.
  • Test delle unità con FakeHealthConnectClient: utilizza la libreria di test per verificare in che modo la tua app gestisce i casi limite, come la revoca delle autorizzazioni o le eccezioni API, senza la necessità di un dispositivo fisico.

Elenco di controllo della qualità

Architettura tipica

Un'implementazione di allenamento include in genere:

Componente Gestisce
Controller sessione Stato della sessione
Timer
Logica di batch
Controller dei tipi di dati
Campionamento della posizione
Livello repository (esegue il wrapping delle operazioni di Health Connect): Inserisci sessione
Inserisci tipi di dati
Inserisci punti del percorso
Leggi i riepiloghi delle sessioni
Livello UI (visualizza): Durata
Tipi di dati in tempo reale
Anteprima mappa
Calcoli divisi
Traccia GPS in tempo reale

Risoluzione dei problemi

Sintomo Possibile causa Risoluzione
Percorso non associato alla sessione Mancata corrispondenza tra ID sessione o intervallo di tempo. Verifica che ExerciseRoute sia scritto con un intervallo di tempo che rientra interamente nella durata di ExerciseSessionRecord. Verifica di utilizzare ID coerenti se fai riferimento alla sessione in un secondo momento. Vedi Registrare i percorsi di allenamento.
Tipi di dati mancanti (ad esempio, battito cardiaco) Autorizzazioni di scrittura mancanti o filtri temporali errati. Verifica di aver richiesto e che l'utente abbia concesso l'autorizzazione per il tipo di dati specifico. Verifica che ReadRecordsRequest utilizzi un TimeRangeFilter che corrisponda alla sessione. Vedi Autorizzazioni.
Impossibile scrivere la sessione Timestamp sovrapposti. Health Connect potrebbe rifiutare i record che si sovrappongono ai dati esistenti della stessa app. Verifica che startTime di una nuova sessione sia successivo a endTime della precedente.
Nessun dato GPS registrato Il servizio in primo piano è stato interrotto o è inattivo. Per raccogliere dati con lo schermo spento, devi utilizzare un servizio in primo piano con l'attributo foregroundServiceType="health" o location.
Vengono visualizzati record duplicati clientRecordId mancante. Assegna un clientRecordId univoco in Metadata di ogni record. In questo modo, Health Connect può eseguire la deduplicazione se gli stessi dati vengono scritti due volte durante un tentativo di sincronizzazione. Vedi Best practice.

Passaggi comuni per il debug

Controlla lo stato delle autorizzazioni. Chiama sempre getPermissionStatus() prima di tentare un'operazione di lettura o scrittura. Gli utenti possono revocare le autorizzazioni nelle impostazioni di sistema in qualsiasi momento.
Verifica la modalità di esecuzione. Se la tua app non raccoglie dati in background, verifica di aver dichiarato le autorizzazioni corrette nel file AndroidManifest.xml e che l'utente non abbia impostato l'app in modalità "Batteria limitata".