Lettura di dati non elaborati

L'esempio seguente mostra come leggere i dati non elaborati nell'ambito del flusso di lavoro comune.

Lettura di dati

Connessione Salute consente alle app di leggere i dati dall'archivio dati quando l'app è in primo piano e in background:

  • Letture in primo piano: in genere puoi leggere i dati da Connessione Salute quando la tua app è in primo piano. In questi casi, potresti usare un servizio in primo piano per eseguire questa operazione se l'utente o il sistema mette la tua app in background durante un'operazione di lettura.

  • Letture in background: se richiedi un'autorizzazione aggiuntiva all'utente, puoi leggere i dati dopo che l'utente o il sistema avrà messo la tua app in background. Vedi l'esempio completo di lettura in background.

Il tipo di dati Passi in Connessione Salute acquisisce il numero di passi che un utente ha compiuto tra le letture. Il conteggio dei passi rappresenta una misurazione comune su piattaforme per la salute, il fitness e il benessere. Health Connect ti consente di leggere e scrivere i dati relativi al conteggio dei passi.

Per leggere i record, crea un ReadRecordsRequest e forniscilo quando chiami readRecords.

Il seguente esempio mostra come leggere i dati del conteggio dei passi per un utente in un determinato lasso di tempo. Per un esempio esteso con SensorManager, consulta la guida ai dati sul conteggio dei passi.

val response = healthConnectClient.readRecords(
    ReadRecordsRequest(
        HeartRateRecord::class,
        timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
    )
)
response.records.forEach { record ->
    /* Process records */
}

Puoi anche leggere i tuoi dati in modo aggregato utilizzando aggregate.

suspend fun readStepsAggregate(startTime: Instant, endTime: Instant): Long {
    val response = healthConnectClient.aggregate(
        AggregateRequest(
            metrics = setOf(StepsRecord.COUNT_TOTAL),
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
    return response[StepsRecord.COUNT_TOTAL] ?: 0L
}

Lettura dei passi da dispositivo mobile

Con Android 14 (livello API 34) e la versione 20 o successive dell'estensione SDK, Health Connect fornisce il conteggio dei passi sul dispositivo. Se a un'app è stata concessa l'autorizzazione READ_STEPS, Health Connect inizia a registrare i passi dal dispositivo Android e gli utenti vedono i dati sui passi aggiunti automaticamente alle voci Passi di Health Connect.

Per verificare se il conteggio dei passi sul dispositivo è disponibile, controlla che il dispositivo utilizzi Android 14 (livello API 34) e abbia almeno la versione 20 dell'estensione SDK:

val isStepTrackingAvailable =
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
        SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20

Se la tua app legge i conteggi dei passi aggregati utilizzando aggregate e non filtra per DataOrigin, i passi sul dispositivo vengono inclusi automaticamente nel totale e non sono necessarie modifiche per l'aggiornamento di giugno 2026.

Modifica dell'attribuzione per i passi sul dispositivo

A partire dall'aggiornamento di giugno 2026, i passi monitorati in modo nativo da Health Connect vengono attribuiti a un nome del pacchetto sintetico (SPN), ad esempio com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.

In precedenza, i passaggi integrati venivano attribuiti al nome del pacchetto android. I dati storici sui passi registrati prima di giugno 2026 conservano il nome del pacchetto android.

I nomi principali del servizio sono specifici per dispositivo e vengono definiti in base all'applicazione per proteggere la privacy degli utenti:

  • Stabile:l'SPN per il dispositivo attuale è stabile per la tua applicazione.
  • Ambito dell'applicazione:diverse applicazioni sullo stesso dispositivo visualizzano SPN diversi per i dati dei passi sul dispositivo.

Query per i passaggi on-device

Poiché gli SPN sono specifici per ambito e dispositivo, non devi codificare i valori SPN. Utilizza invece l'API getCurrentDeviceDataSource per recuperare l'SPN per il dispositivo attuale.

Sebbene il conteggio dei passi sul dispositivo richieda la versione 20 o successive dell'estensione SDK, l'API getCurrentDeviceDataSource() è disponibile su Android 14 (livello API 34) con la versione 22 o successive dell'estensione SDK. Per utilizzare questa API, imposta compileSdkExtension su 22 o versioni successive nel file build.gradle.kts o build.gradle a livello di modulo:

Kotlin

android {
    compileSdk = 35
    compileSdkExtension = 22
}

Alla moda

android {
    compileSdk 35
    compileSdkExtension 22
}

L'API getCurrentDeviceDataSource() non è ancora disponibile nella libreria Jetpack di Connessione Salute. Gli esempi seguenti utilizzano l'API del framework Android, che richiede un callback Executor e un callback OutcomeReceiver:

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.OutcomeReceiver

val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
healthConnectManager?.getCurrentDeviceDataSource(
    context.mainExecutor,
    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
        override fun onResult(result: DeviceDataSource) {
            val currentDeviceSpn = result.deviceDataOrigin.packageName
        }

        override fun onError(error: HealthConnectException) {
            // Handle error
        }
    }
)

Se la tua app deve leggere i passi sul dispositivo o se mostra i dati dei passi suddivisi per applicazione o dispositivo di origine, devi eseguire query per i record in cui DataOrigin è android o corrisponde all'SPN del dispositivo. Se la tua app mostra l'attribuzione per i dati dei passi, utilizza metadata.device per identificare il dispositivo di origine per i singoli record. Per i passaggi on-device identificati da un SPN nei dati aggregati, puoi utilizzare i metadati del dispositivo, ad esempio model o manufacturer di DeviceDataSource per l'attribuzione oppure utilizzare un'etichetta generica come "Il tuo smartphone" per i passaggi on-device.

L'esempio seguente mostra come leggere i dati aggregati del conteggio dei passi sul dispositivo filtrando sia android sia l'SPN del dispositivo corrente:

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.metadata.DataOrigin
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.time.TimeRangeFilter
import java.time.Instant
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine

suspend fun readDeviceStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    context: Context,
    startTime: Instant,
    endTime: Instant
) {
    // 1. Check if SDK Extension 22+ is available for getCurrentDeviceDataSource()
    val isDataSourceApiAvailable =
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 22

    try {
        val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)

        // 2. Safely fetch the package name only if the API is available
        val currentDeviceSpn = if (isDataSourceApiAvailable && healthConnectManager != null) {
            suspendCancellableCoroutine { continuation ->
                healthConnectManager.getCurrentDeviceDataSource(
                    context.mainExecutor,
                    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
                        override fun onResult(result: DeviceDataSource) {
                            continuation.resume(result.deviceDataOrigin.packageName)
                        }

                        override fun onError(error: HealthConnectException) {
                            continuation.resumeWithException(error)
                        }
                    }
                )
            }
        } else {
            null
        }

        val dataOriginFilters = mutableSetOf(DataOrigin("android"))

        // 3. Explicit null-safety check using .let
        currentDeviceSpn?.let {
            dataOriginFilters.add(DataOrigin(it))
        }

        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = dataOriginFilters
            )
        )

        val stepCount = response[StepsRecord.COUNT_TOTAL]

    } catch (e: Exception) {
        // Now this catch block only handles actual runtime exceptions,
        // rather than Errors from missing methods.
    }
}

Conteggio dei passi on-device

  • Utilizzo dei sensori: Health Connect utilizza il sensore TYPE_STEP_COUNTER di SensorManager. Questo sensore è ottimizzato per un basso consumo energetico, il che lo rende ideale per il monitoraggio continuo dei passi in background.
  • Granularità dei dati: per risparmiare batteria, i dati sui passi vengono in genere raggruppati e scritti nel database di Health Connect non più di una volta al minuto.
  • Attribuzione: i passi registrati da questa funzionalità prima di giugno 2026 sono attribuiti al nome del pacchetto android in DataOrigin. Dopo questa data, vengono attribuite a un SPN specifico per dispositivo. Vedi Modifica dell'attribuzione per i passaggi sul dispositivo.
  • Attivazione: il meccanismo di conteggio dei passi sul dispositivo è attivo solo quando ad almeno un'applicazione sul dispositivo è stata concessa l'autorizzazione READ_STEPS in Health Connect.

Esempio di lettura in background

Per leggere i dati in background, dichiara la seguente autorizzazione nel file manifest:

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

Il seguente esempio mostra come leggere i dati del conteggio dei passi in background per un utente in un determinato lasso di tempo utilizzando WorkManager:

class ScheduleWorker(appContext: Context, workerParams: WorkerParameters) :
    CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result {
        val healthConnectClient = HealthConnectClient.getOrCreate(applicationContext)
        // Perform background read logic here
        return Result.success()
    }
}
fun enqueueBackgroundReadWorker(context: Context, healthConnectClient: HealthConnectClient) {
    if (healthConnectClient
            .features
            .getFeatureStatus(
                HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
            ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE
    ) {

        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
}

Il parametro ReadRecordsRequest ha un valore predefinito pageSize di 1000. Se il numero di record in una singola readResponse supera la pageSize della richiesta, devi scorrere tutte le pagine della risposta per recuperare tutti i record utilizzando pageToken. Tuttavia, fai attenzione a evitare problemi di limitazione della frequenza.

Esempio di lettura di pageToken

Ti consigliamo di utilizzare pageToken per leggere i record e recuperare tutti i dati disponibili del periodo di tempo richiesto.

L'esempio seguente mostra come leggere tutti i record finché tutti i token di pagina non sono stati esauriti:

val type = HeartRateRecord::class
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofDays(7))

try {
    var pageToken: String? = null
    do {
        val readResponse =
            healthConnectClient.readRecords(
                ReadRecordsRequest(
                    recordType = type,
                    timeRangeFilter = TimeRangeFilter.between(
                        startTime,
                        endTime
                    ),
                    pageToken = pageToken
                )
            )
        val records = readResponse.records
        // Do something with records
        pageToken = readResponse.pageToken
    } while (pageToken != null)
} catch (quotaError: IllegalStateException) {
    // Backoff
}
Per informazioni sulle best practice per la lettura di set di dati di grandi dimensioni, consulta Pianificare di evitare la limitazione della frequenza.

Leggere i dati scritti in precedenza

Se un'app ha già scritto dei dati su Connessione Salute in passato, è possibile che la stessa app possa leggere anche i dati storici. Ciò è applicabile negli scenari in cui l'app deve essere risincronizzata con Connessione Salute dopo che l'utente l'ha reinstallata.

Si applicano alcune limitazioni di lettura:

  • Per Android 14 e versioni successive

    • Nessun limite storico alla lettura dei propri dati da parte di un'app.
    • Limite di 30 giorni per la lettura di altri dati da parte di un'app.
  • Per Android 13 e versioni precedenti

    • Limite di 30 giorni per la lettura di qualsiasi dato da parte dell'app.

Le limitazioni possono essere rimosse richiedendo un'autorizzazione in lettura.

Per leggere i dati storici, devi indicare il nome del pacchetto come oggetto DataOrigin nel parametro dataOriginFilter della tua ReadRecordsRequest.

L'esempio seguente mostra come indicare il nome di un pacchetto durante la lettura dei record della frequenza cardiaca:

try {
    val response =  healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = HeartRateRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
            dataOriginFilter = setOf(DataOrigin("com.my.package.name"))
        )
    )
    for (record in response.records) {
        // Process each record
    }
} catch (e: Exception) {
    // Run error handling here
}

Leggi l'identificatore univoco del dispositivo (UDI)

Per i record provenienti da dispositivi di grado medico, le applicazioni di lettura possono estrarre la parte dell'identificatore del dispositivo (DI) dell'identificatore univoco del dispositivo (UDI) dai metadati del dispositivo. Per indicazioni su come elaborare queste informazioni e mapparle rispetto ai database normativi, consulta la guida ai metadati.

Leggere i dati più vecchi di 30 giorni

Per impostazione predefinita, tutte le applicazioni possono leggere i dati da Connessione Salute fino a 30 giorni prima del momento in cui è stata concessa la prima autorizzazione.

Se hai bisogno di estendere le autorizzazioni in lettura oltre lelimitazioni predefinite, richiedi PERMISSION_READ_HEALTH_DATA_HISTORY. In caso contrario, senza questa autorizzazione, ogni tentativo di lettura dei record più vecchi di 30 giorni genererà un errore.

Cronologia delle autorizzazioni per un'app eliminata

Se un utente elimina la tua app, tutte le autorizzazioni, inclusa quella della cronologia, vengono revocate. Se l'utente reinstalla la tua app e concede nuovamente l'autorizzazione, si applicano le stesse limitazioni predefinite e la tua app può leggere i dati di Connessione Salute fino a 30 giorni precedenti a questa nuova data.

Ad esempio, supponiamo che l'utente elimini la tua app il 10 maggio 2023 e poi la reinstalli il 15 maggio 2023, concedendo le autorizzazioni in lettura. La data più recente a partire dalla quale la tua app può leggere i dati per impostazione predefinita è il 15 aprile 2023.

Gestire le eccezioni

Connessione Salute genera eccezioni standard per le operazioni CRUD quando si verifica un problema. La tua app deve rilevare e gestire ciascuna di queste eccezioni in modo appropriato.

Ogni metodo in HealthConnectClient elenca le eccezioni che possono essere generate. In generale, l'app deve gestire le seguenti eccezioni:

Tabella 1: eccezioni di Health Connect e best practice consigliate
Eccezione Descrizione Best practice consigliata
IllegalStateException Si è verificato uno dei seguenti scenari:

  • Il servizio Health Connect non è disponibile.
  • La richiesta non è una costruzione valida. Ad esempio, una richiesta aggregata in bucket periodici in cui viene utilizzato un oggetto Instant per timeRangeFilter.

Prima di effettuare una richiesta, gestisci i possibili problemi con gli input. Se possibile, assegna dei valori alle variabili o utilizzali come parametri all'interno di una funzione personalizzata anziché utilizzarli direttamente nelle richieste, in modo da poter applicare strategie di gestione degli errori.
IOException Si sono verificati problemi durante la lettura e la scrittura dei dati dal disco. Per evitare questo problema, ecco alcuni suggerimenti:

  • Esegui il backup di qualsiasi input dell'utente.
  • Preparati a gestire eventuali problemi che si verificano durante le operazioni di scrittura collettiva. Ad esempio, assicurati che la procedura superi il problema ed esegui le operazioni rimanenti.
  • Applica strategie di ripetizione e backoff per gestire i problemi relativi alle richieste.

RemoteException Si sono verificati errori all'interno del servizio sottostante a cui si connette l'SDK o durante la comunicazione con quest'ultimo.

Ad esempio, la tua app sta tentando di eliminare un record con un determinato uid. Tuttavia, l'eccezione viene generata dopo che l'app scopre, controllando il servizio sottostante, che il record non esiste.
Per evitare questo problema, ecco alcuni suggerimenti:

  • Esegui sincronizzazioni regolari tra il datastore della tua app e Health Connect.
  • Applica strategie di ripetizione e backoff per gestire i problemi relativi alle richieste.

SecurityException Si verificano problemi quando le richieste richiedono autorizzazioni non concesse. Per evitare questo problema, assicurati di aver dichiarato l'utilizzo dei tipi di dati di Connessione Salute per l'app pubblicata. Inoltre, devi dichiarare le autorizzazioni di Connessione Salute nel file manifest e nell'attività.