Monitora i dati in background

Gli aggiornamenti passivi dei dati sono adatti per le app che devono monitorare i dati dei servizi sanitari in background. Sono destinati a casi d'uso che durano ore, giorni o anche più a lungo. Se devi archiviare o elaborare i dati sullo stato di integrità quando la tua app non è in esecuzione e l'utente non è esplicitamente coinvolto in un esercizio, utilizza il client passivo di Health Service.

Per esempi di utilizzo dei dati passivi, consulta gli esempi di dati passivi e obiettivi passivi su GitHub.

Aggiungi dipendenze

Per aggiungere una dipendenza a Health Services, devi aggiungere il Repository Maven di Google al tuo progetto. Per ulteriori informazioni, consulta il Repository Maven di Google.

Nel file build.gradle a livello di modulo, aggiungi la dipendenza seguente:

Trendy

dependencies {
    implementation "androidx.health:health-services-client:1.1.0-alpha02"
}

Kotlin

dependencies {
    implementation("androidx.health:health-services-client:1.1.0-alpha02")
}

Verifica le funzionalità

Prima di registrarti per gli aggiornamenti dei dati, verifica che il dispositivo possa fornire il tipo di dati necessario per la tua app. Il controllo delle funzionalità consente di attivare o disattivare determinate funzionalità o modificare l'interfaccia utente della tua app per compensare le funzionalità non disponibili.

val healthClient = HealthServices.getClient(this /*context*/)
val passiveMonitoringClient = healthClient.passiveMonitoringClient
lifecycleScope.launchWhenCreated {
    val capabilities = passiveMonitoringClient.capabilities.await()
    // Supported types for passive data collection
    supportsHeartRate =
        DataType.HEART_RATE_BPM in capabilities.supportedDataTypesPassiveMonitoring
    // Supported types for PassiveGoals
    supportsStepsGoal =
        DataType.STEPS_DAILY in capabilities.supportedDataTypesPassiveGoals
}

Registrati per i dati passivi

Puoi ricevere dati passivi tramite un servizio, un callback o entrambi. Un servizio consente alla tua app di ricevere dati in background quando nessuna parte dell'app è visibile in primo piano. I dati ricevuti in background vengono pubblicati in batch. Il callback riceve i dati a una velocità leggermente più veloce, ma solo quando l'app è in esecuzione e il callback viene avvisato correttamente.

Qualunque metodo utilizzi, crea prima un PassiveListenerConfig che stabilisca i tipi di dati da ricevere, come mostrato nell'esempio seguente:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDataTypes(setOf(DataType.HEART_RATE_BPM))
    .build()

Per ricevere i dati utilizzando un callback, definisci e registra il callback, come mostrato nell'esempio seguente:

val passiveListenerCallback: PassiveListenerCallback = object : PassiveListenerCallback {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerCallback(
    passiveListenerConfig,
    passiveListenerCallback
)

// To remove the listener
passiveMonitoringClient.clearPassiveListenerCallbackAsync()

L'utilizzo di un servizio è simile, ma invece di creare una classe derivata da PassiveListenerCallback, deriva da PassiveListenerService, come mostrato nell'esempio seguente:

class PassiveDataService : PassiveListenerService() {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerServiceAsync(
    PassiveDataService::class.java,
    passiveListenerConfig
)

Quindi, dichiara il servizio nel file AndroidManifest.xml. Richiedi un'autorizzazione per i servizi sanitari, che garantisca che solo i servizi sanitari possano associarsi al servizio:

<service android:name=".PassiveDataService"
    android:permission="com.google.android.wearable.healthservices.permission.PASSIVE_DATA_BINDING"
    android:exported="true" />

Interpreta l'ora

I dati ricevuti da Health Services vengono raggruppati, quindi potresti ricevere punti dati di tipo diverso o più punti dati dello stesso tipo nello stesso batch. Per determinare l'ordine corretto degli eventi, utilizza i timestamp inclusi in questi oggetti anziché l'ora in cui sono stati ricevuti dall'app.

Ottieni i timestamp per ogni DataPoint calcolando prima il timestamp di avvio, come mostrato nell'esempio seguente:

val bootInstant =
    Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())

Questo valore può essere quindi trasmesso a getStartInstant() o getEndInstant().

Ripristina registrazioni dopo l'avvio

Le registrazioni dei dati passive non vengono mantenute tra un riavvio e l'altro. Per ricevere i dati dopo il riavvio di un dispositivo, ricrea le registrazioni utilizzando un BroadcastReceiver che ascolti la trasmissione di sistema ACTION_BOOT_COMPLETED.

Nel destinatario, non tentare di ripristinare direttamente le registrazioni. Delega invece questa funzionalità a un worker WorkManager. All'avvio del dispositivo, Health Services potrebbe impiegare almeno 10 secondi per accettare una richiesta di registrazione dei dati passiva e potrebbe superare il tempo di esecuzione consentito di BroadcastReceiver. Al contrario, i worker WorkManager hanno un limite di esecuzione di 10 minuti.

Il seguente snippet mostra l'aspetto di un elemento BroadcastReceiver:

class StartupReceiver : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       if (intent.action != Intent.ACTION_BOOT_COMPLETED) return


       // TODO: Check permissions first
       WorkManager.getInstance(context).enqueue(
           OneTimeWorkRequestBuilder<RegisterForPassiveDataWorker>().build()
       )
   }
}

class RegisterForPassiveDataWorker(
   private val appContext: Context,
   workerParams: WorkerParameters
) : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       runBlocking {
           HealthServices.getClient(appContext)
                .passiveMonitoringClient
                .setPassiveListenerCallback(...)
       }
       return Result.success()
   }
}

Per fare in modo che il sistema esegua questo codice all'avvio del dispositivo, apporta due modifiche al file AndroidManifest.xml.

Innanzitutto, aggiungi la seguente autorizzazione come utente secondario di <manifest>:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

In secondo luogo, aggiungi il seguente filtro per intent del destinatario come elemento secondario di <application>:

<receiver
    android:name=".StartupReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Stato dell'attività

Il client passivo può anche fornire informazioni generali sullo stato dell'utente, ad esempio se l'utente sta dormendo o meno. Per ricevere questi aggiornamenti, procedi nel seguente modo:

  1. Richiedi l'autorizzazione ACTIVITY_RECOGNITION.
  2. Chiama setShouldUserActivityInfoBeRequested(true) nello strumento per la creazione di PassiveListenerConfig.

Esegui l'override del metodo onUserActivityInfoReceived() nel callback o nel servizio e utilizza il valore UserActivityInfo restituito, come mostrato nell'esempio seguente:

override fun onUserActivityInfoReceived(info: UserActivityInfo) {
    val stateChangeTime: Instant = info.stateChangeTime // may be in the past!
    val userActivityState: UserActivityState = info.userActivityState
    if (userActivityState == UserActivityState.USER_ACTIVITY_ASLEEP) {
        // ...
    }
}

Obiettivi passivi

Puoi configurare un client passivo per inviare una notifica all'app quando vengono raggiunti degli obiettivi passivi, ad esempio se l'utente completa 10.000 passaggi al giorno.

A questo scopo, crea un obiettivo, come illustrato nell'esempio seguente:

val dailyStepsGoal by lazy {
    val condition = DataTypeCondition(
        dataType = DataType.STEPS_DAILY,
        threshold = 10_000, // Trigger every 10000 steps
        comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
    )
    PassiveGoal(condition)
}

Aggiungi questo obiettivo a PassiveListenerConfig, come mostrato nel seguente esempio:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDailyGoals(setOf(dailyStepsGoal))
    .build()

Esegui l'override del metodo onGoalCompleted() nel callback o nel servizio e utilizza il valore PassiveGoal restituito, come mostrato nell'esempio seguente:

override fun onGoalCompleted(goal: PassiveGoal) {
    when (goal.dataTypeCondition.dataType) {
        DataType.STEPS_DAILY -> {
            // ...
        }
    }
}