Las actualizaciones de datos pasivos son adecuadas para las apps que necesitan supervisar los datos de los Servicios de salud en segundo plano. Se crearon para casos de uso que abarcan horas, días o incluso más tiempo. Si necesitas almacenar o procesar datos de salud cuando tu app no está en ejecución y el usuario no participa explícitamente en un ejercicio, debes usar el cliente pasivo de los Servicios de salud.
Para ver ejemplos de uso de datos pasivo, consulta el Datos pasivos y Objetivos pasivos de muestra en GitHub.
Cómo agregar dependencias
Para agregar una dependencia en los Servicios de salud, debes agregar el repositorio de Maven de Google a tu proyecto. Para obtener información relacionada, consulta el repositorio de Maven de Google.
En el archivo build.gradle
a nivel del módulo, agrega la siguiente dependencia:
Groovy
dependencies { implementation "androidx.health:health-services-client:1.1.0-alpha03" }
Kotlin
dependencies { implementation("androidx.health:health-services-client:1.1.0-alpha03") }
Cómo verificar las capacidades
Antes de registrarte para recibir actualizaciones de datos, verifica que el dispositivo pueda proporcionar el tipo de datos que necesita tu app. La verificación de capacidades te permite habilitar o inhabilitar ciertas funciones, así como modificar la IU de tu app para compensar las funciones que no están disponibles.
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
}
Cómo registrarse para usar datos pasivos
Puedes recibir datos pasivos a través de un servicio, una devolución de llamada o ambos. Un servicio permite que tu app reciba datos en segundo plano cuando no hay una parte visible de ella en primer plano. Cuando recibes datos en segundo plano, estos se entregan en lotes. La devolución de llamada recibe datos a una velocidad un poco más rápida, pero solo mientras se ejecuta la app y se notifica correctamente dicha devolución de llamada.
Sin importar el método que uses, primero crea un PassiveListenerConfig
que determine los tipos de datos que quieres recibir, como se muestra en el siguiente ejemplo:
val passiveListenerConfig = PassiveListenerConfig.builder()
.setDataTypes(setOf(DataType.HEART_RATE_BPM))
.build()
Para recibir datos con una devolución de llamada, define y registra la devolución de llamada como se muestra en el siguiente ejemplo:
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()
El uso de un servicio es similar, pero en lugar de crear una clase derivada de PassiveListenerCallback
, deriva de PassiveListenerService
, como se muestra en el siguiente ejemplo:
class PassiveDataService : PassiveListenerService() {
override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
// TODO: Do something with dataPoints
}
}
passiveMonitoringClient.setPassiveListenerServiceAsync(
PassiveDataService::class.java,
passiveListenerConfig
)
Luego, declara el servicio en tu archivo AndroidManifest.xml
. Esto requiere un permiso de Servicios de salud, que garantiza que solo los Servicios de salud puedan vincularse al servicio:
<service android:name=".PassiveDataService"
android:permission="com.google.android.wearable.healthservices.permission.PASSIVE_DATA_BINDING"
android:exported="true" />
Tiempo de interpretación
Los datos que recibes de los servicios de salud se agrupan en lotes, por lo que puedes recibir datos de diferentes tipos o varios datos del mismo tipo en el mismo lote. Usa las marcas de tiempo incluidas en estos objetos en lugar de la hora en que la app las recibió para determinar el orden correcto de los eventos.
A fin de obtener marcas de tiempo para cada DataPoint
, primero calcula la marca de tiempo de inicio, como se muestra en el siguiente ejemplo:
val bootInstant =
Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())
Este valor se puede pasar a getStartInstant()
o getEndInstant()
.
Cómo restablecer registros después del inicio
Los registros de datos pasivos no persisten entre reinicios. Para recibir datos después de reiniciar un dispositivo, vuelve a crear tus registros con un BroadcastReceiver
que escuche la transmisión del sistema ACTION_BOOT_COMPLETED
.
En el receptor, no intentes restablecer los registros directamente. En su lugar, debes delegar esta funcionalidad a un trabajador WorkManager
. Cuando se inicia el dispositivo, los Servicios de salud pueden demorar 10 segundos o más en confirmar una solicitud de registro de datos pasivos, lo que puede exceder el tiempo de ejecución permitido de un BroadcastReceiver
. Por el contrario, los trabajadores WorkManager
tienen un límite de ejecución de 10 minutos.
En el siguiente fragmento, se muestra cómo se vería 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()
}
}
Para que el sistema ejecute este código cuando se inicie el dispositivo, realiza dos cambios en el archivo file AndroidManifest.xml
.
Primero, agrega el siguiente permiso como elemento secundario de <manifest>
:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
En segundo lugar, agrega el siguiente filtro de intents del receptor como elemento secundario de <application>
:
<receiver
android:name=".StartupReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Estado de la actividad
El cliente pasivo también puede proporcionar información general sobre el estado del usuario, por ejemplo, si el usuario está durmiendo. Para recibir estas actualizaciones, sigue estos pasos:
- Solicita el permiso
ACTIVITY_RECOGNITION
. - Llama a
setShouldUserActivityInfoBeRequested(true)
en el compilador dePassiveListenerConfig
.
Anula el método onUserActivityInfoReceived()
en tu devolución de llamada o servicio, y usa el UserActivityInfo
que se muestra, como se indica en el siguiente ejemplo:
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) {
// ...
}
}
Objetivos pasivos
Puedes configurar un cliente pasivo para que notifique a la app cuando se alcanzan los objetivos pasivos, como cuando el usuario completa 10,000 pasos en un día.
Para ello, crea un objetivo, como se muestra en el siguiente ejemplo:
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)
}
Agrega este objetivo a tu PassiveListenerConfig
, como se muestra en el siguiente ejemplo:
val passiveListenerConfig = PassiveListenerConfig.builder()
.setDailyGoals(setOf(dailyStepsGoal))
.build()
Anula el método onGoalCompleted()
en tu devolución de llamada o servicio, y usa el PassiveGoal
que se muestra, como se indica en el siguiente ejemplo:
override fun onGoalCompleted(goal: PassiveGoal) {
when (goal.dataTypeCondition.dataType) {
DataType.STEPS_DAILY -> {
// ...
}
}
}
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado.
- Ejercicio y datos activos
- Cómo comenzar a usar tarjetas
- Cómo agregar una pantalla de presentación