Desarrolla experiencias de entrenamiento con Health Connect

Esta guía es compatible con la versión 1.1.0 o posterior de Health Connect.

Si quieres crear una experiencia de entrenamiento en tu app, puedes usar Health Connect para hacer lo siguiente:

  • Cómo escribir sesiones de ejercicio
  • Escribir rutas de entrenamiento
  • Registrar métricas de entrenamiento, como la frecuencia cardíaca, la velocidad y la distancia
  • Leer datos de entrenamiento de otras apps

En esta guía, se describe cómo compilar estas funciones de entrenamiento, incluidos los tipos de datos, la ejecución en segundo plano, los permisos, los flujos de trabajo recomendados y las prácticas recomendadas.

Descripción general: Crea un registro de entrenamiento integral

Puedes crear una experiencia integral de seguimiento de entrenamientos con Health Connect siguiendo estos pasos principales:

  • Implementar correctamente los permisos según los permisos de salud
  • Grabar sesiones con ExerciseSessionRecord
  • Escribir datos de entrenamiento de forma coherente durante la sesión
  • Administrar correctamente la ejecución en segundo plano para verificar la captura continua de datos
  • Leer datos de sesiones para obtener resúmenes y análisis posteriores al entrenamiento

Este flujo de trabajo habilita la interoperabilidad con otras apps de Health Connect y verifica el acceso a los datos controlado por el usuario.

Antes de comenzar

Antes de implementar las funciones de entrenamiento, haz lo siguiente:

Conceptos clave

Health Connect representa los datos de entrenamiento con algunos componentes principales. Un objeto ExerciseSessionRecord actúa como el registro central de un entrenamiento y contiene detalles como las horas de inicio o finalización, y el tipo de ejercicio. Durante una sesión, se pueden registrar varios tipos de datos, como HeartRateRecord o SpeedRecord. En el caso de las actividades al aire libre, ExerciseRoute almacena datos de GPS, que se vinculan a la sesión correspondiente.

Sesiones de ejercicio

ExerciseSessionRecord es el registro central de los datos de entrenamiento, que representa una sola sesión de entrenamiento. Cada registro almacena lo siguiente:

  • startTime
  • endTime
  • exerciseType
  • Metadatos de la sesión opcionales (título, notas)

Un ExerciseSessionRecord también puede contener rutas de ejercicio, vueltas y segmentos como parte de sus datos. Además, se pueden registrar otros tipos de datos, como HeartRateRecord o SpeedRecord, durante una sesión y asociarlos a ella.

Tipos de datos asociados

Los datos asociados con las sesiones de entrenamiento se representan con tipos de registros individuales. Entre los tipos más comunes, se incluyen los siguientes:

Para obtener una lista completa de los tipos de datos, consulta Tipos de datos de Health Connect.

Rutas de ejercicio

Puedes asociar una ruta con entrenamientos al aire libre usando ExerciseRoute. Las rutas constan de objetos ExerciseRoute.Location secuenciales, cada uno de los cuales contiene lo siguiente:

  • Latitud y longitud
  • Altitud opcional
  • Rumbo opcional
  • Información sobre la precisión
  • Marca de tiempo

Vincula las rutas de sesión

Un ExerciseRoute contiene los datos de ubicación secuenciales de una sesión de ejercicio. No se trata como un registro independiente en Health Connect. En su lugar, proporcionas datos de ExerciseRoute cuando insertas o actualizas un ExerciseSessionRecord.

Consideraciones de desarrollo

Las apps de seguimiento de entrenamiento suelen necesitar ejecutarse durante períodos prolongados, con frecuencia en segundo plano cuando la pantalla está apagada. Cuando crees tus funciones de entrenamiento, es importante que consideres cómo administrar la ejecución en segundo plano y solicitar los permisos necesarios para los datos de entrenamiento.

Ejecución en segundo plano

Las apps de entrenamiento suelen ejecutarse con la pantalla apagada. En este estado, debes usar lo siguiente:

  • Servicios en primer plano para el muestreo de ubicación y sensores
  • WorkManager para la escritura o sincronización diferidas
  • Estrategias de procesamiento por lotes para la escritura de registros regulares

Mantén la continuidad usando el mismo ID de sesión en todas las escrituras.

Permisos

Tu app debe solicitar los permisos pertinentes de Health Connect antes de leer o escribir datos de entrenamiento. Los permisos comunes para los entrenamientos incluyen sesiones de ejercicio, rutas de ejercicio y métricas, como la frecuencia cardíaca o la velocidad. Estas son algunas de ellas:

  • Sesiones de ejercicio: Permisos de lectura y escritura para ExerciseSessionRecord.
  • Rutas de ejercicio: Permisos de lectura y escritura para ExerciseRoute.
  • Frecuencia cardíaca: Permisos de lectura y escritura para HeartRateRecord.
  • Velocidad: Permisos de lectura y escritura para SpeedRecord.
  • Distancia: Permisos de lectura y escritura para DistanceRecord.
  • Calorías: Permisos de lectura y escritura para TotalCaloriesBurnedRecord.
  • Desnivel acumulado: Permisos de lectura y escritura para ElevationGainedRecord.
  • Cadencia de pasos: Permisos de lectura y escritura para StepsCadenceRecord.
  • Potencia: Permisos de lectura y escritura para PowerRecord.
  • Pasos: Permisos de lectura y escritura para StepsRecord.

En el siguiente ejemplo, se muestra cómo solicitar varios permisos para una sesión de entrenamiento que incluye datos de ruta, frecuencia cardíaca, distancia, calorías, velocidad y pasos:

Después de crear una instancia de cliente, tu app debe solicitarle permisos al usuario. Los usuarios deben poder otorgar o rechazar permisos en cualquier momento. Para hacerlo, crea un conjunto de permisos para los tipos de datos necesarios. Primero, asegúrate de que los permisos del conjunto se declaren en tu manifiesto de Android.

val permissions =
    setOf(
        HealthPermission.getReadPermission(ExerciseSessionRecord::class),
        HealthPermission.getWritePermission(ExerciseSessionRecord::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)
    )
Usa getGrantedPermissions para ver si tu app ya tiene otorgados los permisos necesarios. De lo contrario, usa createRequestPermissionResultContract para solicitarlos. Se mostrará la pantalla de permisos de Health Connect.
val permissions = setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class),
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class)
    )

val requestPermissionsLauncher = rememberLauncherForActivityResult(
    contract = PermissionController.createRequestPermissionResultContract()
) { grantedPermissions ->
    if (grantedPermissions.containsAll(permissions)) {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") }
    } else {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") }
    }
}
Debido a que los usuarios pueden otorgar o revocar permisos en cualquier momento, tu app debe verificar los permisos cada vez que los use y controlar las situaciones en las que se pierda el permiso.

Para solicitar permisos, llama a la función checkPermissionsAndRun:

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

Si solo necesitas solicitar permisos para un tipo de datos, como la frecuencia cardíaca, incluye únicamente ese tipo de datos en tu conjunto de permisos:

El acceso a la frecuencia cardíaca está protegido por los siguientes permisos:

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

Si deseas agregar la función de frecuencia cardíaca a tu app, comienza por solicitar permisos para el tipo de datos HeartRateRecord.

Este es el permiso que debes declarar para poder escribir la frecuencia cardíaca:

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

Para leer la frecuencia cardíaca, debes solicitar los siguientes permisos:

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

Cómo implementar una sesión de entrenamiento

En esta sección, se describe el flujo de trabajo recomendado para registrar datos de entrenamiento.

Inicia la sesión

Para crear un entrenamiento nuevo, sigue estos pasos:

  1. Genera un ID de sesión único: Verifica que este ID sea estable. Si el proceso de tu app se cierra y se reinicia, debes poder reanudarlo con el mismo ID para evitar sesiones fragmentadas.
  2. Establece un metadata.clientRecordId para evitar duplicados durante los reintentos de sincronización.
  3. Escribe un ExerciseSessionRecord: Incluye la hora de inicio.
  4. Comienza a recopilar el tipo de datos y los datos de GPS: Comienza a recopilarlos solo después de que se inicialice correctamente el registro de la sesión.

Ejemplo:

val sessionClientId = UUID.randomUUID().toString()
val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime)

val session =   ExerciseSessionRecord(
    startTime = startTime,
    startZoneOffset = zoneOffset,
    endTime = startTime.plusSeconds(3600),
    endZoneOffset = zoneOffset,
    exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
    metadata = Metadata.activelyRecorded(clientRecordId = sessionClientId, device = Device(type = Device.TYPE_PHONE)),
)

healthConnectClient.insertRecords(listOf(session))

Registra rutas de ejercicio

Para obtener más información sobre la guía de lectura, consulta Cómo leer datos sin procesar.

Cuando grabes una ruta de ejercicio, debes agrupar tus datos por lotes. Esto significa que, en lugar de guardar cada punto de GPS a medida que se genera, recopilas un grupo de puntos y los guardas todos juntos en una sola llamada.

Esto es importante porque cada vez que tu app lee o escribe en Health Connect, usa un poco de batería y potencia de procesamiento.

En el siguiente código, se muestra cómo registrar datos por lotes:

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

Cómo finalizar una sesión

Después de detener la recopilación de datos, haz lo siguiente:

  • Actualiza el registro: Tu app actualiza ExerciseSessionRecord con un endTime.
  • Finaliza los datos: De manera opcional, calcula los valores de resumen (como la distancia total o el ritmo promedio) y escríbelos como registros adicionales.
val finishedSession = session.copy(endTime = Instant.now())
healthConnectClient.updateRecords(listOf(finishedSession))

Cómo leer datos de entrenamiento

Las apps pueden leer las sesiones de ejercicio y sus datos asociados para resumir la actividad, proporcionar estadísticas de salud o sincronizar datos con un servidor externo. Por ejemplo, puedes leer un ExerciseSessionRecord y, luego, consultar el HeartRateRecord o DistanceRecord que se produjo durante ese mismo intervalo.

Si necesitas sincronizar los datos de entrenamiento con un servidor de backend o mantener actualizado el almacén de datos de tu app con Health Connect, usa ChangeLogs. Esto te permite recuperar una lista de registros insertados, actualizados o borrados desde un momento específico, lo que es más eficiente que hacer un seguimiento manual de los cambios o leer todos los datos de forma repetida. Para obtener más información, consulta Cómo sincronizar datos con Health Connect.

Lectura de sesiones

Para leer las sesiones de ejercicio, usa un ReadRecordsRequest con ExerciseSessionRecord como tipo. Por lo general, se filtra por un período específico.

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

Leer rutas

Aunque los datos de ExerciseRoute se escriben como parte de una sesión de ejercicio, se deben leer por separado. Usa el método getExerciseRoute() con el ID de la sesión para leer sus datos de ruta:

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

Tipos de datos de lectura

Para leer datos detallados específicos (como la frecuencia cardíaca) que se produjeron durante una sesión, usa los valores startTime y endTime de la sesión para filtrar la solicitud de ese tipo de datos.

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

Prácticas recomendadas

Sigue estos lineamientos para mejorar la confiabilidad de los datos y la experiencia del usuario:

  • Escribe con frecuencia durante el monitoreo de actividades: Para el monitoreo de actividades, escribe los datos a medida que estén disponibles o en un intervalo máximo de 15 minutos.
  • Usa WorkManager para las sincronizaciones en segundo plano: Usa WorkManager para las escrituras diferidas. Intenta establecer un intervalo de 15 minutos para lograr un equilibrio entre los datos en tiempo real y la eficiencia de la batería.
  • Agrupa las solicitudes de escritura: No escribas cada evento de sensor de forma individual. Divide tus solicitudes en fragmentos. Health Connect controla hasta 1,000 registros por solicitud de escritura.
  • Mantén los IDs de sesión estables y únicos: Usa identificadores coherentes para tus sesiones. Si se edita o actualiza una sesión, usar el mismo ID evita que se trate como una sesión nueva y separada.
  • Usa el procesamiento por lotes para los tipos de datos y los puntos de ruta: Para reducir la sobrecarga de entrada y salida, y preservar la duración de la batería, agrupa tus datos en una sola llamada a insertRecords en lugar de escribir cada punto de forma individual.
  • Evita escribir datos duplicados: Usa IDs de cliente: Cuando crees registros, establece un metadata.clientRecordId. Health Connect usa este valor para identificar registros únicos. Si intentas escribir un registro con un clientRecordId que ya existe, Health Connect ignorará el duplicado o actualizará el registro existente en lugar de crear uno nuevo. Establecer un metadata.clientRecordId es la forma más eficaz de evitar duplicados durante los reintentos de sincronización o las reinstalaciones de la 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"
        )
    )
  • Verifica los datos existentes: Antes de sincronizar, consulta el período para ver si ya existen registros de tu app.
  • Valida la precisión del GPS: Filtra las muestras de GPS de baja precisión (por ejemplo, puntos con un radio de precisión horizontal alto) antes de escribir en el ExerciseRoute para verificar que el mapa se vea limpio y profesional.
  • Asegúrate de que las marcas de tiempo no se superpongan: Verifica que no se inicie una sesión nueva antes de que finalice la anterior. Las sesiones superpuestas pueden causar conflictos en los paneles de fitness y los cálculos de resumen.
  • Proporciona justificaciones claras para el permiso: Usa el flujo de Permission.createIntent para explicar por qué tu app necesita acceder a los datos de salud, por ejemplo: "Para supervisar tus tendencias del estrés vascular y proporcionar estadísticas".
  • Admite la pausa y la reanudación: Verifica que tu app controle las pausas correctamente. Cuando un usuario pausa la actividad, deja de recopilar puntos de ruta y tipos de datos para que el ritmo y la duración promedio sigan siendo precisos.
  • Prueba sesiones de larga duración: Supervisa el consumo de batería durante sesiones que duren varias horas para verificar que el intervalo de procesamiento por lotes y el uso de sensores no agoten la batería del dispositivo.
  • Alinea las marcas de tiempo con las frecuencias de los sensores: Haz coincidir las marcas de tiempo de los registros con la frecuencia real de los sensores para mantener la alta fidelidad de los datos.

Pruebas

Para verificar la exactitud de los datos y una experiencia del usuario de alta calidad, sigue estas estrategias de prueba y consulta la documentación oficial de Prueba los casos de uso principales.

Herramientas de verificación

  • Health Connect Toolbox: Usa esta app complementaria para inspeccionar registros de forma manual, borrar datos de prueba y simular cambios en la base de datos. Es la mejor manera de verificar que tus registros se almacenen correctamente.
  • Pruebas de unidades con FakeHealthConnectClient: Usa la biblioteca de pruebas para verificar cómo tu app controla los casos extremos, como la revocación de permisos o las excepciones de la API, sin necesidad de un dispositivo físico.

Lista de verificación de calidad

Arquitectura típica

La implementación de un entrenamiento suele incluir lo siguiente:

Componente Administra
Controlador de sesiones Estado de la sesión
Temporizador
Lógica de procesamiento por lotes
Controladores de tipos de datos
Muestreo de ubicación
Capa del repositorio (un wrapper de las operaciones de Health Connect): Inserta sesiones
Inserta tipos de datos
Inserta puntos de ruta
Lee resúmenes de sesiones
Capa de la IU (pantallas): Duración
Tipos de datos en tiempo real
Vista previa del mapa
Cálculos de tramos
Registro de GPS en tiempo real

Solución de problemas

Síntoma Causa posible Solución
La ruta no está asociada a la sesión No coinciden el ID de sesión o el período. Verifica que la ExerciseRoute esté escrita con un período que se encuentre completamente dentro de la duración de la ExerciseSessionRecord. Verifica que estés usando IDs coherentes si haces referencia a la sesión más adelante. Consulta Cómo grabar rutas de ejercicio.
Tipos de datos faltantes (por ejemplo, frecuencia cardíaca) Faltan permisos de escritura o los filtros de tiempo son incorrectos. Verifica que hayas solicitado el permiso de tipo de datos específico y que el usuario lo haya otorgado. Verifica que tu ReadRecordsRequest use un TimeRangeFilter que coincida con la sesión. Consulta Permisos.
No se puede escribir la sesión Marcas de tiempo superpuestas Es posible que Health Connect rechace los registros que se superpongan con los datos existentes de la misma app. Valida que el startTime de una sesión nueva sea posterior al endTime de la anterior.
No se registraron datos de GPS El servicio en primer plano se cerró o está inactivo. Para recopilar datos mientras la pantalla está apagada, debes usar un servicio en primer plano con el atributo foregroundServiceType="health" o de ubicación.
Aparecen registros duplicados Falta clientRecordId. Asigna un clientRecordId único en el Metadata de cada registro. Esto permite que Health Connect realice la desduplicación si se escriben los mismos datos dos veces durante un reintento de sincronización. Consulta las prácticas recomendadas.

Pasos comunes para la depuración

Verifica el estado de los permisos. Siempre llama a getPermissionStatus() antes de intentar una operación de lectura o escritura. Los usuarios pueden revocar los permisos en la configuración del sistema en cualquier momento.
Verifica el modo de ejecución. Si tu app no recopila datos en segundo plano, verifica que hayas declarado los permisos correctos en tu archivo AndroidManifest.xml y que el usuario no haya colocado la app en el modo "Restringido por batería".