Desarrolla experiencias de entrenamiento con Health Connect

Si quieres crear una experiencia de entrenamiento en tu app, puedes usar Health Connect para realizar las siguientes acciones:

  • Cómo escribir sesiones de ejercicio
  • Grabar 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, y se abarcan 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: Cómo compilar un Comprehensive Workout Tracker

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

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

Este flujo de trabajo permite 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 y 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 su sesión correspondiente.

Sesiones de ejercicio

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

  • startTime
  • endTime
  • exerciseType
  • Metadatos de 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 con 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
  • Orientación opcional
  • Información de precisión
  • Marca de tiempo

Cómo vincular rutas de sesión

Un objeto 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, proporciona 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, a menudo 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. Cuando se encuentran en este estado, debes usar lo siguiente:

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

Mantén la continuidad conservando el ID de sesión coherente 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. Se incluye lo siguiente:

  • 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.
  • Elevación ganada: Permisos de lectura y escritura para ElevationGainedRecord.
  • Cadencia de pasos: Permisos de lectura y escritura para StepsCadenceRecord.
  • Alimentación: Permisos de lectura y escritura para PowerRecord.
  • Pasos: Permisos de lectura y escritura para StepsRecord.

A continuación, se muestra un ejemplo de 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.

// Create a set of permissions for required data types
val PERMISSIONS =
    setOf(
  HealthPermission.getReadPermission(ExerciseSessionRecord::class),
  HealthPermission.getWritePermission(ExerciseSessionRecord::class),
  HealthPermission.getReadPermission(ExerciseRoute::class),
  HealthPermission.getWritePermission(ExerciseRoute::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.

// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()

val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
  if (granted.containsAll(PERMISSIONS)) {
    // Permissions successfully granted
  } else {
    // Lack of required permissions
  }
}

suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
  val granted = healthConnectClient.permissionController.getGrantedPermissions()
  if (granted.containsAll(PERMISSIONS)) {
    // Permissions already granted; proceed with inserting or reading data
  } else {
    requestPermissions.launch(PERMISSIONS)
  }
}

Dado que los usuarios pueden otorgar o revocar permisos en cualquier momento, tu app debe verificar los permisos cada vez antes de usarlos y controlar las situaciones en las que se pierden los permisos.

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

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

Si solo necesitas solicitar permisos para un solo tipo de datos, como la frecuencia cardíaca, incluye solo 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

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

Implementa 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 se detiene y reinicia el proceso de tu app, debes poder reanudar el uso del 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 datos de tipo de datos y de GPS: Solo comienza a recopilarlos después de que se inicialice correctamente el registro de la sesión.

Ejemplo:

val sessionId = UUID.randomUUID().toString()
val sessionClientId = UUID.randomUUID().toString()

val session = ExerciseSessionRecord(
    id = sessionId,
    exerciseType = ExerciseType.EXERCISE_TYPE_RUNNING,
    startTime = Instant.now(),
    endTime = null,
    metadata = Metadata(clientRecordId = sessionClientId),
)

healthConnectClient.insertRecords(listOf(session))

Cómo registrar rutas de ejercicio

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

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

Esto es importante porque, cada vez que tu app lee o escribe en Health Connect, usa una pequeña cantidad de batería y capacidad de procesamiento.

En el siguiente código, se muestra cómo registrar 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 el DistanceRecord que ocurrió durante ese mismo intervalo de tiempo.

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.

Leer sesiones

Para leer 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 específicos detallados (como la frecuencia cardíaca) que se produjeron durante una sesión, usa 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:

  • Frecuencia de escritura
    • Seguimiento activo(en primer plano): Para los entrenamientos activos, escribe los datos a medida que estén disponibles o en un intervalo máximo de 15 minutos.
    • Sincronización en segundo plano: Usa WorkManager para 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.
    • Procesamiento por lotes: No escribas cada evento del sensor de forma individual. Fragmenta tus solicitudes. 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 un entrenamiento nuevo y separado.
  • Usa el procesamiento por lotes para los tipos de datos y los puntos de ruta: Para reducir la sobrecarga de entrada/salida y preservar la duración de la batería, agrupa tus puntos de 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 campo 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 la sincronización, 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, los puntos con un radio de precisión horizontal alto) antes de escribir en ExerciseRoute para verificar que el mapa se vea limpio y profesional.

  • Asegúrate de que las marcas de tiempo no se superpongan: Verifica que una nueva sesión no comience antes de que finalice la anterior. Las sesiones superpuestas pueden causar conflictos en los paneles de actividad física y en los cálculos de resumen.

  • Proporciona explicaciones claras sobre los permisos: Usa el flujo de Permission.createIntent para explicar por qué tu app necesita acceso a los datos de salud, por ejemplo, "Para trazar un mapa de tus carreras y calcular las calorías quemadas".

  • 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 la 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 las sesiones que duran varias horas para verificar que el intervalo de procesamiento por lotes y el uso del sensor no agoten el dispositivo.

  • Alinea las marcas de tiempo con las tasas de los sensores: Haz coincidir las marcas de tiempo de tus registros con la frecuencia real de tus sensores (por ejemplo, 1 Hz para el GPS) para mantener la alta fidelidad de los datos.

Prueba

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

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

Por lo general, la implementación de un entrenamiento incluye lo siguiente:

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

Solución de problemas

Síntoma Causa posible Resolución
La ruta no está asociada a la sesión No coincide el ID de sesión o el período. Verifica que el ExerciseRoute esté escrito con un período que se encuentre completamente dentro de la duración del ExerciseSessionRecord. Verifica que uses IDs coherentes si haces referencia a la sesión más adelante. Consulta Cómo grabar rutas de ejercicio.
Faltan tipos de datos (por ejemplo, frecuencia cardíaca) Faltan permisos de escritura o los filtros de tiempo son incorrectos. Verifica que hayas solicitado y que el usuario haya otorgado el permiso de tipo de datos específico. Verifica que tu ReadRecordsRequest use un TimeRangeFilter que coincida con la sesión. Consulta Permisos.
No se puede escribir en 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 Se cerró o se encuentra inactivo el servicio en primer plano. 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 eliminación de duplicados si los mismos datos se escriben dos veces durante un reintento de sincronización. Consulta las prácticas recomendadas.

Pasos comunes de depuración

  • Verifica el estado del permiso: 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 AndroidManifest.xml y que el usuario no haya colocado la app en el modo "Batería restringida".