Desarrolla experiencias de sueño con Health Connect

Si quieres crear una experiencia de seguimiento del sueño en tu app, puedes usar Health Connect para realizar las siguientes acciones:

  • Cómo registrar sesiones de sueño
  • Escribe datos de la etapa del sueño
  • Escribir datos de sueño, como la frecuencia cardíaca, la saturación de oxígeno y la frecuencia respiratoria
  • Leer datos de sueño de otras apps

En esta guía, se describe cómo compilar estas funciones de sueño, 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 dispositivo de monitoreo del sueño integral

Para crear una experiencia integral de monitoreo del sueño con Health Connect, sigue estos pasos principales:

  • Implementar correctamente los permisos según los permisos de salud
  • Graba sesiones con SleepSessionRecord.
  • Escribir tipos de datos como las fases de sueño, la frecuencia cardíaca y la saturación de oxígeno de forma coherente durante la sesión
  • Administrar correctamente la ejecución en segundo plano para verificar la captura continua de datos durante la noche
  • Datos de la sesión de lectura para análisis y resúmenes posteriores al sueño

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 sueño, haz lo siguiente:

Conceptos clave

Health Connect representa los datos de sueño con algunos componentes principales. Un SleepSessionRecord actúa como el registro central del sueño, ya que contiene detalles como las horas de inicio o finalización y las fases del sueño. Durante una sesión, se pueden registrar varios tipos de datos, como HeartRateRecord o OxygenSaturationRecord.

Sesiones de sueño

Los datos de sueño se representan con SleepSessionRecord. Cada registro almacena lo siguiente:

  • startTime
  • endTime
  • stages: Es una lista de SleepSessionRecord.Stage que incluye el sueño profundo, ligero, REM y de vigilia.
  • Metadatos de sesión opcionales (título, notas)

Las apps pueden escribir varios tipos de datos asociados con una sesión.

Tipos de datos

Entre los tipos de datos comunes que se registran durante una sesión de sueño, se incluyen los siguientes:

Cada tipo de datos se almacena como un registro individual.

Consideraciones de desarrollo

Las apps de monitoreo del sueño suelen necesitar ejecutarse durante períodos prolongados, a menudo en segundo plano cuando la pantalla está apagada. Cuando crees tus funciones de sueño, es importante que consideres cómo administrar la ejecución en segundo plano y solicitar los permisos necesarios para los datos de sueño.

Ejecución en segundo plano

Las apps de monitoreo del sueño suelen ejecutarse durante la noche con la pantalla apagada. Cuando se encuentran en este estado, debes usar lo siguiente:

  • Servicios en primer plano para la recopilación de datos
  • WorkManager para escritura o sincronización diferida
  • Estrategias de procesamiento por lotes para escrituras de registros regulares de datos detallados, como la frecuencia cardíaca

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 sueño. Para obtener una lista completa de los tipos de datos, consulta Tipos de datos de Health Connect. Los permisos comunes para el sueño incluyen sesiones de sueño y métricas como la frecuencia cardíaca o la saturación de oxígeno.

El acceso al sueño está protegido por los siguientes permisos:

  • android.permission.health.READ_SLEEP
  • android.permission.health.WRITE_SLEEP

Para agregar la función de sueño a tu app, comienza por solicitar permisos para el tipo de datos SleepSession.

Este es el permiso que debes declarar para poder escribir el sueño:

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

Para leer el sueño, debes solicitar los siguientes permisos:

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

A continuación, se muestra un ejemplo de cómo solicitar permisos para una sesión de sueño que incluye datos de frecuencia cardíaca, saturación de oxígeno y frecuencia respiratoria:

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(SleepSessionRecord::class),
  HealthPermission.getWritePermission(SleepSessionRecord::class),
  HealthPermission.getReadPermission(HeartRateRecord::class),
  HealthPermission.getWritePermission(HeartRateRecord::class),
  HealthPermission.getReadPermission(OxygenSaturationRecord::class),
  HealthPermission.getWritePermission(OxygenSaturationRecord::class),
  HealthPermission.getReadPermission(RespiratoryRateRecord::class),
  HealthPermission.getWritePermission(RespiratoryRateRecord::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.

Implementa una sesión de sueño

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

Para alinear tipos de datos como HeartRateRecord o OxygenSaturationRecord con una sesión de sueño, regístralos con marcas de tiempo que se encuentren entre startTime y endTime de la sesión. Health Connect no usa un identificador de sesión para vincular las sesiones de sueño con datos detallados. En cambio, la asociación es implícita a través de intervalos de tiempo superpuestos. Cuando lees datos de sueño, puedes usar el intervalo de tiempo de una sesión para consultar los tipos de datos asociados, como se muestra en Cómo leer datos de sueño.

Cómo escribir una sesión

Si bien los datos detallados, como la frecuencia cardíaca, se pueden registrar durante una sesión de sueño, el SleepSessionRecord en sí solo se debe escribir en Health Connect una vez que finalice la sesión, por ejemplo, cuando el usuario se despierte. El registro debe incluir la sesión startTime, endTime y una lista de objetos SleepSessionRecord.Stage registrados durante la sesión, ya que SleepSessionRecord requiere que endTime se encuentre después de startTime.

Para escribir una sesión de sueño, haz lo siguiente:

  1. Genera un ID de registro de cliente único.
  2. Cuando el usuario se despierta o se detiene el monitoreo del sueño, recopila todas las etapas del sueño y construye un objeto SleepSessionRecord.
  3. Inserta el registro con insertRecords.

Ejemplo:

val clientRecordId = UUID.randomUUID().toString()
val sessionStartTime = LocalDateTime.of(2023, 10, 30, 22, 0).toInstant(ZoneOffset.UTC)
val sessionEndTime = LocalDateTime.of(2023, 10, 31, 7, 0).toInstant(ZoneOffset.UTC)

val stages = mutableListOf<SleepSessionRecord.Stage>()
// Add recorded stages, for example:
stages.add(SleepSessionRecord.Stage(
    startTime = sessionStartTime.plusSeconds(3600),
    endTime = sessionStartTime.plusSeconds(7200),
    stage = SleepSessionRecord.STAGE_TYPE_LIGHT)
)
stages.add(SleepSessionRecord.Stage(
    startTime = sessionStartTime.plusSeconds(7200),
    endTime = sessionStartTime.plusSeconds(10800),
    stage = SleepSessionRecord.STAGE_TYPE_DEEP)
)
// ... other stages

val session = SleepSessionRecord(
    startTime = sessionStartTime,
    startZoneOffset = ZoneOffset.UTC,
    endTime = sessionEndTime,
    endZoneOffset = ZoneOffset.UTC,
    stages = stages,
    metadata = Metadata(clientRecordId = clientRecordId)
)

healthConnectClient.insertRecords(listOf(session))

Cómo leer datos de sueño

Las apps pueden leer las sesiones de sueño 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 SleepSessionRecord y, luego, consultar el HeartRateRecord que ocurrió durante ese mismo intervalo de tiempo.

Sesión de lectura con datos asociados

Puedes leer las sesiones de sueño con un ReadRecordsRequest que tenga SleepSessionRecord como tipo de registro, filtrado por un intervalo de tiempo. Para leer los datos asociados de una sesión determinada, realiza una segunda solicitud para el tipo de datos seleccionado, como HeartRateRecord, filtrando por el startTime y el endTime de la sesión de sueño.

En el siguiente ejemplo, se muestra cómo leer las sesiones de sueño con datos de frecuencia cardíaca asociados para un período determinado:

suspend fun readSleepSessionsWithAssociatedData(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    val response = healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = SleepSessionRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )

    for (sleepRecord in response.records) {
        // Process each session
        val stages = sleepRecord.stages
        val notes = sleepRecord.notes

        // To read specific granular data (like heart rate) that occurred during
        // this session, use the session's startTime and endTime to filter
        // the request for that data type.
        val hrResponse = healthConnectClient.readRecords(
            ReadRecordsRequest(
                recordType = HeartRateRecord::class,
                timeRangeFilter = TimeRangeFilter.between(
                    sleepRecord.startTime,
                    sleepRecord.endTime
                )
            )
        )
        for (heartRateRecord in hrResponse.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 el seguimiento activo del sueño, escribe los datos a medida que estén disponibles o con 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 una sesión de sueño nueva y separada.
  • Usa el procesamiento por lotes para los tipos de datos: Para reducir la sobrecarga de entrada/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 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 = RespiratoryRateRecord(
        rate = 16.0,
        time = time,
        zoneOffset = ZoneOffset.UTC,
        metadata = Metadata(
            // Use a unique ID from your own database
            clientRecordId = "respiratory_rate_20231030_1"
        )
    )
    
  • Verifica los datos existentes: Antes de la sincronización, consulta el período para ver si ya existen registros de tu app.

  • 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 justificaciones claras para los permisos: Usa el flujo de Permission.createIntent para explicar por qué tu app necesita acceso a los datos de salud, por ejemplo, "Para analizar tus patrones de sueño".

  • 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 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 del monitoreo del sueño 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
Recopilación de datos
Capa del repositorio (encapsula las operaciones de Health Connect): Insertar sesión
Insertar tipos de datos
Insertar fases de sueño
Leer resúmenes de sesiones
Capa de la IU (pantallas): Duración
Tipos de datos en vivo
Visualización de las fases del sueño

Solución de problemas

Síntoma Causa posible Resolución
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 sensores durante el sueño Se cerró o se encuentra inactivo el servicio en primer plano. Para recopilar datos de sensores durante la noche mientras la pantalla está apagada, puedes usar un servicio en primer plano con foregroundServiceType="health".
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".