Esta guía es compatible con la versión 1.1.0-alpha12 de Health Connect.
En esta guía, se explica el proceso de escritura o actualización de datos de Health Connect.
Controla los valores cero
Algunos tipos de datos, como los pasos, la distancia o las calorías, pueden tener un valor de 0
.
Solo escribe valores cero cuando reflejen una inactividad real mientras el usuario usaba el dispositivo. No escribas valores cero si no se usó el dispositivo, faltan datos o se agotó la batería. En esos casos, omite el registro para evitar datos engañosos.
Cómo configurar la estructura de los datos
Antes de escribir los datos, primero debemos configurar los registros. Para más de 50 tipos de datos, cada uno tiene sus respectivas estructuras. Consulta la referencia de Jetpack para obtener más detalles sobre los tipos de datos disponibles.
Registros básicos
El tipo de datos de Pasos de Health Connect captura la cantidad de pasos que dio un usuario entre cada medición. El recuento de pasos representa una medición común en todas las plataformas de salud, actividad física y bienestar.
En el siguiente ejemplo, se muestra cómo configurar los datos del recuento de pasos:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
Registros con unidades de medida
Health Connect puede almacenar valores junto con sus unidades de medida para proporcionar una mayor precisión. Un ejemplo es el tipo de datos de Nutrición que es amplio y extenso. Incluye una gran variedad de campos nutricionales opcionales, que van desde carbohidratos totales hasta vitaminas. Cada dato representa los nutrientes que posiblemente se consumieron como parte de una comida o un alimento.
En el tipo de datos, todos los nutrientes se representan en unidades de Mass
, mientras que energy
se representa en una unidad de Energy
.
En el siguiente ejemplo, se muestra cómo configurar los datos de nutrición de un usuario que comió una banana:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(1))
val banana = NutritionRecord(
name = "banana",
energy = 105.0.kilocalories,
dietaryFiber = 3.1.grams,
potassium = 0.422.grams,
totalCarbohydrate = 27.0.grams,
totalFat = 0.4.grams,
saturatedFat = 0.1.grams,
sodium = 0.001.grams,
sugar = 14.0.grams,
vitaminB6 = 0.0005.grams,
vitaminC = 0.0103.grams,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
device = Device(type = Device.TYPE_PHONE)
)
)
Registros con datos de series
Health Connect puede almacenar una lista de datos de series. Un ejemplo es el tipo de datos de Frecuencia cardíaca que captura una serie de muestras de latidos detectadas entre lecturas.
En este tipo de datos, el parámetro samples
se representa con una lista de muestras de frecuencia cardíaca. Cada muestra contiene un valor beatsPerMinute
y un valor time
.
En el siguiente ejemplo, se muestra cómo configurar los datos de series de frecuencia cardíaca:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
val heartRateRecord = HeartRateRecord(
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
// records 10 arbitrary data, to replace with actual data
samples = List(10) { index ->
HeartRateRecord.Sample(
time = startTime + Duration.ofSeconds(index.toLong()),
beatsPerMinute = 100 + index.toLong(),
)
},
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
))
Solicita permisos al usuario
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(HeartRateRecord::class),
HealthPermission.getWritePermission(HeartRateRecord::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)
}
}
Como los usuarios pueden otorgar o revocar permisos en cualquier momento, tu app necesita verificar en forma periódica los permisos otorgados y controlar situaciones en las que el permiso se pierde.
Escribe datos
Uno de los flujos de trabajo comunes de Health Connect es la escritura de datos. Para agregar registros, usa insertRecords
.
En el siguiente ejemplo, se muestra cómo escribir datos mediante la inserción de recuentos de pasos:
suspend fun insertSteps(healthConnectClient: HealthConnectClient) {
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
try {
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
healthConnectClient.insertRecords(listOf(stepsRecord))
} catch (e: Exception) {
// Run error handling here
}
}
Cómo actualizar datos
Si necesitas cambiar uno o más registros, en especial cuando necesitas sincronizar el almacén de datos de tu app con datos de Health Connect, puedes actualizar los datos. Existen dos maneras de actualizar los datos existentes, que dependen del identificador que se use para encontrar los registros.
Metadatos
Vale la pena examinar la clase Metadata
primero, ya que es necesario cuando se actualizan datos. Durante la creación, cada Record
de Health Connect tiene un campo metadata
. Las siguientes propiedades son relevantes para la sincronización:
Propiedades | Descripción |
---|---|
id
|
Cada Record de Health Connect tiene un valor id único.Health Connect lo propaga automáticamente cuando insertas un registro nuevo. |
lastModifiedTime
|
Cada Record también registra la última vez que se modificó el registro.Health Connect lo propaga automáticamente. |
clientRecordId
|
Cada Record puede tener un ID único asociado para usarlo como referencia en el almacén de datos de tu app.
Tu app proporciona este valor. |
clientRecordVersion
|
Cuando un registro tiene clientRecordId , se puede usar clientRecordVersion para permitir que los datos permanezcan sincronizados con la versión del almacén de datos de tu app.Tu app proporciona este valor. |
Actualización después de la lectura por rango de tiempo
Para actualizar los datos, primero prepara los registros que requieres. Si es necesario, realiza cambios en los registros. Luego, llama a updateRecords
para realizar los cambios.
En el siguiente ejemplo, se muestra cómo actualizar los datos. Para este propósito, cada registro tiene sus valores de desplazamiento de zona ajustados en PST.
suspend fun updateSteps(
healthConnectClient: HealthConnectClient,
prevRecordStartTime: Instant,
prevRecordEndTime: Instant
) {
try {
val request = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = StepsRecord::class, timeRangeFilter = TimeRangeFilter.between(
prevRecordStartTime, prevRecordEndTime
)
)
)
val newStepsRecords = arrayListOf<StepsRecord>()
for (record in request.records) {
// Adjusted both offset values to reflect changes
val sr = StepsRecord(
count = record.count,
startTime = record.startTime,
startZoneOffset = record.startTime.atZone(ZoneId.of("PST")).offset,
endTime = record.endTime,
endZoneOffset = record.endTime.atZone(ZoneId.of("PST")).offset,
metadata = record.metadata
)
newStepsRecords.add(sr)
}
healthConnectClient.updateRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
Cómo insertar y actualizar mediante el ID de registro del cliente
Si usas los valores opcionales de ID de registro del cliente y de versión del registro del cliente, te recomendamos que uses insertRecords
en lugar de updateRecords
.
La función insertRecords
tiene la capacidad de insertar y actualizar datos.
Si los datos existen en Health Connect según el conjunto determinado de IDs de registro del cliente, se reemplazan. De lo contrario, se escriben como datos nuevos.
Esta situación es útil cuando necesitas sincronizar los datos del almacén de datos de tu app con Health Connect.
En el siguiente ejemplo, se muestra cómo realizar una inserción y actualización de los datos que se extraen del almacén de datos de la app:
suspend fun pullStepsFromDatastore() : ArrayList<StepsRecord> {
val appStepsRecords = arrayListOf<StepsRecord>()
// Pull data from app datastore
// ...
// Make changes to data if necessary
// ...
// Store data in appStepsRecords
// ...
var sr = StepsRecord(
metadata = Metadata.autoRecorded(
clientRecordId = "Your client record ID",
device = Device(type = Device.TYPE_WATCH)
),
// Assign more parameters for this record
)
appStepsRecords.add(sr)
// ...
return appStepsRecords
}
suspend fun upsertSteps(
healthConnectClient: HealthConnectClient,
newStepsRecords: ArrayList<StepsRecord>
) {
try {
healthConnectClient.insertRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
Después de eso, podrás llamar a estas funciones en tu subproceso principal.
upsertSteps(healthConnectClient, pullStepsFromDatastore())
Verificación de valores en la versión de registro del cliente
Si tu proceso de inserción y actualización de datos incluye la versión de registro del cliente, Health Connect realiza verificaciones de comparación en los valores de clientRecordVersion
. Si la versión de los datos insertados es superior a la versión de los datos existentes, se produce la inserción y la actualización. De lo contrario, el proceso ignorará el cambio y el valor seguirá siendo el mismo.
Para incluir el control de versiones en tus datos, debes proporcionar a Metadata.clientRecordVersion
un valor Long
basado en tu lógica de control de versiones.
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 100L,
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
clientRecordId = "Your supplied record ID",
clientRecordVersion = 0L, // Your supplied record version
device = Device(type = Device.TYPE_WATCH)
)
)
Las inserciones y las actualizaciones no aumentan automáticamente version
, lo que evita cualquier instancia inesperada de reemplazo de datos. De esta manera, debes proporcionarle manualmente un valor más alto.
Prácticas recomendadas para escribir datos
Las apps solo deben escribir datos de origen propio en Health Connect.
Si los datos de tu app se importaron desde otra app, la responsabilidad de escribir sus propios datos en Health Connect recae en la otra app.
También es una buena idea implementar una lógica que maneje excepciones de escritura, como datos que están fuera de los límites o un error interno del sistema. Puedes aplicar tus estrategias de retirada y reintento en un mecanismo de programación de trabajos. Si la escritura en Health Connect no tiene éxito, asegúrate de que la app pueda superar ese punto de exportación. Recuerda informar y registrar errores para facilitar el diagnóstico.
Cuando realizas un seguimiento de los datos, hay algunas sugerencias que puedes seguir según la forma en que tu app escribe datos.
Administración de zonas horarias
Cuando escribas registros basados en el tiempo, evita establecer compensaciones en zoneOffset.UTC de forma predeterminada, ya que esto puede generar marcas de tiempo inexactas cuando los usuarios se encuentren en otras zonas. En su lugar, calcula la compensación en función de la ubicación real del dispositivo.
Puedes recuperar la zona horaria del dispositivo con ZoneId.systemDefault()
.
val endTime = Instant.now()
val startTime = endTime.minus(java.time.Duration.ofDays(1))
val stepsRecords = mutableListOf<StepsRecord>()
var sampleTime = startTime
val minutesBetweenSamples = 15L
while (sampleTime < endTime) {
// Get the default ZoneId then convert it to an offset
val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(sampleTime)
stepsRecords += StepsRecord(
startTime = sampleTime.minus(java.time.Duration.ofMinutes(minutesBetweenSamples)),
startZoneOffset = zoneOffset,
endTime = sampleTime,
endZoneOffset = zoneOffset,
count = Random.nextLong(1, 100),
metadata = Metadata.unknownRecordingMethod(),
)
sampleTime = sampleTime.plus(java.time.Duration.ofMinutes(minutesBetweenSamples))
}
healthConnectClient.insertRecords(
stepsRecords
)
Para obtener más detalles, consulta la documentación de ZoneId
.
Seguimiento pasivo
Esto incluye las apps que realizan un seguimiento pasivo de la salud o la actividad física en segundo plano de forma continua, como el registro de pasos o de la frecuencia cardíaca.
La app debe escribir datos de forma periódica en Health Connect de las siguientes maneras:
- En cada sincronización, solo escribe datos nuevos y actualiza los que se modificaron desde la última vez que se sincronizó.
- Fragmenta las solicitudes a un máximo de 1,000 registros por solicitud de escritura.
- Usa
WorkManager
para programar tareas periódicas en segundo plano, con un período de al menos 15 minutos. - Restringe las tareas para que se ejecuten solo cuando el dispositivo esté inactivo y no tenga poca batería.
val constraints = Constraints.Builder()
.requiresBatteryNotLow()
.requiresDeviceIdle(true)
.build()
val writeDataWork = PeriodicWorkRequestBuilder<WriteDataToHealthConnectWorker>(
15,
TimeUnit.MINUTES,
5,
TimeUnit.MINUTES
)
.setConstraints(constraints)
.build()
Seguimiento activo
Esto incluye las apps que realizan un seguimiento basado en eventos, como el ejercicio y el sueño, o las entradas manuales del usuario, como la nutrición. Estos registros se crean cuando la app está en primer plano o en eventos poco frecuentes en los que se usa algunas veces al día.
Asegúrate de que la app no mantenga Health Connect en ejecución durante todo el evento.
Los datos deben escribirse en Health Connect de una de estas dos maneras:
- Sincroniza los datos de Health Connect una vez completado el evento. Por ejemplo, sincroniza los datos cuando el usuario finaliza una sesión de ejercicio registrada.
- Programa una tarea única con
WorkManager
para sincronizar los datos más tarde.
Prácticas recomendadas para la granularidad y la frecuencia de las escrituras
Cuando escribas datos en Health Connect, usa la resolución adecuada. Usar la resolución adecuada ayuda a reducir la carga de almacenamiento y, al mismo tiempo, mantener datos coherentes y precisos. La resolución de datos abarca 2 aspectos:
- Frecuencia de escritura: Es la frecuencia con la que tu aplicación envía datos nuevos a Health Connect. Por ejemplo, escribe datos nuevos cada 15 minutos.
- Nivel de detalle de los datos escritos: Indica con qué frecuencia se muestrearon los datos que se enviaron. Por ejemplo, escribir muestras de frecuencia cardíaca cada 5 s. No todos los tipos de datos requieren la misma tasa de muestreo. No es muy beneficioso actualizar los datos del recuento de pasos por segundo, en comparación con una cadencia menos frecuente, como cada 60 segundos. Sin embargo, las tasas de muestreo más altas pueden dar a los usuarios un panorama más detallado de sus datos de salud y actividad física. Las frecuencias de muestreo deben alcanzar un equilibrio entre detalle y rendimiento.
Escribir datos supervisados a lo largo del día
En el caso de los datos recopilados de forma continua, como los pasos, tu aplicación debe escribir en Health Connect al menos cada 15 minutos durante el día.
Tipo de datos |
Unidad |
Esperado |
Ejemplo |
Pasos |
los pasos |
Cada 1 minuto |
23:14 a 23:15: 5 pasos 23:16 a 23:17: 22 pasos 23:17 a 23:18: 8 pasos |
StepsCadence |
pasos/min |
Cada 1 minuto |
23:14 - 23:15 - 5 ppm 23:16 a 23:17: 22 spm 23:17 a 23:18: 8 spm |
Impulsos en silla de ruedas |
empuja |
Cada 1 minuto |
23:14 a 23:15: 5 empujes 23:16 a 23:17: 22 empujes 23:17 - 23:18: 8 notificaciones push |
ActiveCaloriesBurned |
Calorías |
Cada 15 minutos |
23:15 a 23:30: 2 calorías 23:30 a 23:45: 25 calorías 23:45 a 00:00: 5 calorías |
TotalCaloriesBurned |
Calorías |
Cada 15 minutos |
23:15 a 23:30: 16 calorías 23:30 a 23:45: 16 calorías 23:45 a 00:00: 16 calorías |
Distancia |
km/min |
Cada 1 minuto |
23:14 a 23:15: 0.008 km 23:16 - 23:16 - 0.021 km 23:17 a 23:18, 0.012 km |
ElevationGained |
m |
Cada 1 minuto |
20:36 a 20:37: 3.048 m 20:39 - 20:40 - 3.048 m 23:23 a 23:24, 9.144 m |
FloorsClimbed |
pisos |
Cada 1 minuto |
23:14 a 23:15: 5 pisos 23:16 - 23:16 - 22 pisos 23:17 a 23:18, 8 pisos |
HeartRate |
ppm |
4 veces por minuto |
6:11:15 a.m., 55 ppm 6:11:30 a.m. - 56 ppm 6:11:45 a.m. - 56 bpm 6:12:00 a.m. - 55 ppm |
HeartRateVariabilityRmssd |
ms |
Cada 1 minuto |
6:11 a.m. - 23 ms |
RespiratoryRate |
respiraciones/minuto |
Cada 1 minuto |
23:14 a 23:15: 60 respiraciones por minuto 23:16 - 23:16 - 62 respiraciones por minuto 23:17 - 23:18: 64 respiraciones por minuto |
OxygenSaturation |
% |
Cada 1 hora |
6:11, 95.208% |
Escribir sesiones
Los datos se deben escribir en Health Connect al final de la sesión de entrenamiento o sueño.
Como mínimo, tu aplicación debe seguir las orientaciones de la columna Esperado de la siguiente tabla. Cuando sea posible, sigue las mejores prácticas recomendadas.
Sesiones de ejercicio y sueño
En el siguiente ejemplo, se muestra cómo escribir datos durante un ejercicio:
Tipo de datos |
Unidad |
Esperado |
Saludos |
Ejemplo |
Pasos |
los pasos |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 5 pasos 23:16 a 23:17: 22 pasos 23:17 a 23:18: 8 pasos |
StepsCadence |
pasos/min |
Cada 1 minuto |
Cada 1 segundo |
23:14-23:15: 35 ppm 23:16 - 23:17 - 37 spm 23:17 - 23:18 - 40 s.p.m. |
Impulsos en silla de ruedas |
empuja |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 5 notificaciones push 23:16 a 23:17: 22 empujes 23:17 - 23:18: 8 notificaciones push |
CyclingPedalingCadence |
rpm |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 65 rpm 23:16 - 23:17 - 70 rpm 23:17 - 23:18 - 68 rpm |
Energía |
vatios |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 250 vatios 23:16 a 23:17, 255 vatios 23:17 a 23:18: 245 vatios |
Velocidad |
km/min |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 0.3 km/min 23:16 a 23:17: 0.4 km/min 23:17 a 23:18, -0.4 km/min |
Distancia |
km/m |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 0.008 km 23:16 - 23:16 - 0.021 km 23:17 a 23:18, 0.012 km |
ActiveCaloriesBurned |
Calorías |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 20 calorías 23:16 a 23:17: 20 calorías 23:17 a 23:18: 25 calorías |
TotalCaloriesBurned |
Calorías |
Cada 1 minuto |
Cada 1 segundo |
23:14 a 23:15: 36 calorías 23:16 a 23:17: 36 calorías 23:17 a 23:18: 41 calorías |
ElevationGained |
m |
Cada 1 minuto |
Cada 1 segundo |
20:36 a 20:37: 3.048 m 20:39 - 20:40 - 3.048 m 23:23 a 23:24, 9.144 m |
ExerciseRoutes |
lat/lng/alt |
Cada 3 a 5 segundos |
Cada 1 segundo |
|
HeartRate |
ppm |
4 veces por minuto |
Cada 1 segundo |
23:14 a 23:15: 150 ppm |
En el siguiente ejemplo, se muestra cómo escribir datos durante o después de una sesión de sueño:
Tipo de datos |
Unidad |
Muestras esperadas |
Ejemplo |
Etapas de sueño |
etapa |
Período granular por fase del sueño |
23:46 a 23:50: En vigilia 23:50 a 23:56: Sueño ligero 23:56 a 00:16: Sueño profundo |
RestingHeartRate |
ppm |
Un solo valor diario (se espera que se proporcione a primera hora de la mañana) |
6:11 a.m., 60 ppm |
OxygenSaturation |
% |
Un solo valor diario (se espera que se proporcione a primera hora de la mañana) |
6:11, 95.208% |
Eventos polideportivos
Este enfoque utiliza tipos y estructuras de datos existentes, y verifica la compatibilidad con las implementaciones y los lectores de datos actuales de Health Connect. Este es un enfoque común que adoptan las plataformas de fitness.
En el siguiente ejemplo, se muestra cómo escribir datos para un triatlón:
val swimStartTime = Instant.parse("2024-08-22T08:00:00Z")
val swimEndTime = Instant.parse("2024-08-22T08:30:00Z")
val bikeStartTime = Instant.parse("2024-08-22T08:40:00Z")
val bikeEndTime = Instant.parse("2024-08-22T09:40:00Z")
val runStartTime = Instant.parse("2024-08-22T09:50:00Z")
val runEndTime = Instant.parse("2024-08-22T10:20:00Z")
val swimSession = ExerciseSessionRecord(
startTime = swimStartTime,
endTime = swimEndTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
val bikeSession = ExerciseSessionRecord(
startTime = bikeStartTime,
endTime = bikeEndTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
val runSession = ExerciseSessionRecord(
startTime = runStartTime,
endTime = runEndTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
healthConnectClient.insertRecords(listOf(swimSession, bikeSession, runSession))