Surveiller les données en arrière-plan

Les mises à jour passives des données sont adaptées aux applications qui doivent surveiller les données de Services Santé en arrière-plan. Elles sont destinées à des cas d'utilisation qui durent plusieurs heures ou jours, voire plus longtemps. Si vous devez stocker ou traiter des données de santé lorsque votre application n'est pas exécutée et que l'utilisateur n'est pas explicitement impliqué dans un exercice, utilisez le client passif des Services Santé.

Pour obtenir des exemples d'utilisation passive de données, consultez les exemples de données passives et d'objectifs passifs sur GitHub.

Ajouter des dépendances

Pour ajouter une dépendance à Services Santé, vous devez ajouter le dépôt Maven de Google à votre projet. Pour en savoir plus, consultez les informations sur le dépôt Maven de Google.

Dans le fichier build.gradle au niveau du module, ajoutez la dépendance suivante :

Groovy

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

Kotlin

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

Vérifier les fonctionnalités

Avant de vous inscrire aux mises à jour de données, vérifiez que l'appareil peut fournir le type de données dont votre application a besoin. La vérification des fonctionnalités vous permet d'activer ou de désactiver certaines fonctionnalités, ou de modifier l'UI de votre application pour compenser les fonctionnalités indisponibles.

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
}

S'inscrire aux données passives

Vous pouvez recevoir des données passives via un service et/ou un rappel. Un service permet à votre application de recevoir des données en arrière-plan lorsqu'aucune partie de votre application n'est visible au premier plan. Les données reçues en arrière-plan sont transmises par lots. Le rappel reçoit des données à un débit légèrement plus rapide, mais uniquement lorsque l'application est en cours d'exécution et que le rappel a bien été notifié.

Quelle que soit la méthode utilisée, créez d'abord une configuration PassiveListenerConfig qui détermine les types de données à recevoir, comme illustré dans l'exemple suivant :

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

Pour recevoir des données à l'aide d'un rappel, définissez et enregistrez le rappel comme indiqué dans l'exemple suivant :

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'utilisation d'un service est similaire, mais au lieu de créer une classe dérivée de PassiveListenerCallback, vous devez dériver de PassiveListenerService, comme illustré dans l'exemple suivant :

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

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

Ensuite, déclarez le service dans votre fichier AndroidManifest.xml. Exigez une autorisation Services Santé pour garantir que seuls les éléments Services Santé peuvent s'associer au service :

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

Interpréter l'heure

Les données que vous recevez de Services Santé sont regroupées. Vous pouvez donc recevoir des points de données de types différents ou plusieurs points de données du même type dans un même lot. Pour déterminer l'ordre correct des événements, utilisez les codes temporels inclus dans ces objets plutôt que l'heure de réception de votre application.

Obtenez les codes temporels de chaque DataPoint en calculant d'abord le code temporel de démarrage, comme indiqué dans l'exemple suivant :

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

Cette valeur peut ensuite être transmise à getStartInstant() ou à getEndInstant().

Restaurer les inscriptions après le démarrage

Les inscriptions aux données passives ne persistent pas lors des redémarrages. Pour recevoir des données après le redémarrage d'un appareil, recréez vos inscriptions à l'aide d'un BroadcastReceiver qui écoute l'annonce du système ACTION_BOOT_COMPLETED.

Dans le récepteur, ne tentez pas de restaurer les inscriptions directement. Cette fonctionnalité doit plutôt être déléguée à un Worker WorkManager. Au démarrage de l'appareil, Services Santé peut prendre 10 secondes ou davantage pour accuser réception d'une requête d'inscription aux données passives, ce qui peut dépasser le délai d'exécution autorisé d'un BroadcastReceiver. En revanche, les Workers WorkManager ont une limite d'exécution de 10 minutes.

L'extrait suivant montre à quoi peut ressembler un 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()
   }
}

Pour faire en sorte que le système exécute ce code au démarrage de l'appareil, apportez deux modifications à votre fichier AndroidManifest.xml.

Tout d'abord, ajoutez l'autorisation suivante en tant qu'enfant de <manifest> :

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

Ensuite, ajoutez le filtre d'intent récepteur suivant en tant qu'enfant de <application> :

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

État d'activité

Le client passif peut également fournir des informations générales sur l'état de l'utilisateur (par exemple, si l'utilisateur est en train de dormir). Pour recevoir ces informations, procédez comme suit :

  1. Demandez l'autorisation ACTIVITY_RECOGNITION.
  2. Appelez setShouldUserActivityInfoBeRequested(true) dans le compilateur PassiveListenerConfig.

Remplacez la méthode onUserActivityInfoReceived() dans votre rappel ou votre service, et utilisez le UserActivityInfo renvoyé, comme dans l'exemple suivant :

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) {
        // ...
    }
}

Objectifs passifs

Vous pouvez configurer un client passif pour avertir l'application lorsque des objectifs passifs sont atteints (par exemple, lorsque l'utilisateur a effectué 10 000 pas dans la journée).

Pour ce faire, créez un objectif, comme illustré dans l'exemple suivant :

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)
}

Ajoutez cet objectif à votre PassiveListenerConfig, comme illustré dans l'exemple suivant :

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

Remplacez la méthode onGoalCompleted() dans votre rappel ou votre service, et utilisez le PassiveGoal renvoyé, comme dans l'exemple suivant :

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