Это руководство совместимо с Health Connect версии 1.1.0-alpha12 .
В этом руководстве описывается процесс записи или обновления данных в Health Connect.
Обработка нулевых значений
Некоторые типы данных, такие как шаги, расстояние или калории, могут иметь значение 0
Записывайте нулевые значения только в тех случаях, когда это отражает фактическое бездействие во время ношения устройства пользователем. Не записывайте нулевые значения, если устройство не было надето, данные отсутствуют или аккумулятор разряжен. В таких случаях пропустите запись, чтобы избежать ввода неверных данных.
Настройте структуру данных
Перед записью данных необходимо настроить записи. Более 50 типов данных имеют свою собственную структуру. Подробнее о доступных типах данных см. в справочнике Jetpack .
Основные записи
Тип данных «Шаги» в Health Connect фиксирует количество шагов, пройденных пользователем между измерениями. Количество шагов — это распространённый показатель на платформах для здоровья, фитнеса и оздоровления.
В следующем примере показано, как настроить данные о количестве шагов:
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)
)
)
Записи с единицами измерения
Health Connect может хранить значения вместе с единицами измерения для обеспечения точности. Одним из примеров является тип данных «Питание» (Nutrition) , который является обширным и всеобъемлющим. Он включает в себя широкий спектр дополнительных полей с данными о питательных веществах, от общего содержания углеводов до витаминов. Каждая точка данных представляет собой количество питательных веществ, которые потенциально были потреблены в составе приёма пищи или продукта.
В этом типе данных все питательные вещества представлены в единицах Mass
, а energy
представлена в единицах Energy
.
В следующем примере показано, как установить данные о питании для пользователя, съевшего банан:
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)
)
)
Записи с серийными данными
Health Connect может хранить список серий данных. Одним из примеров является тип данных «Частота сердечных сокращений» , который фиксирует серию измерений частоты сердечных сокращений между измерениями.
В этом типе данных samples
параметров представлены списком выборок частоты сердечных сокращений . Каждая выборка содержит значение beatsPerMinute
и значение time
.
В следующем примере показано, как настроить данные серии пульса:
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)
))
Запросить разрешения у пользователя
После создания клиентского экземпляра ваше приложение должно запрашивать разрешения у пользователя. Пользователи должны иметь возможность предоставлять или отклонять разрешения в любое время.
Для этого создайте набор разрешений для необходимых типов данных. Убедитесь, что разрешения в наборе предварительно объявлены в манифесте 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)
)
Используйте getGrantedPermissions
, чтобы проверить, предоставлены ли приложению необходимые разрешения. Если нет, запросите их с помощью createRequestPermissionResultContract
. Откроется экран разрешений 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)
}
}
Поскольку пользователи могут предоставлять или отзывать разрешения в любое время, ваше приложение должно периодически проверять наличие предоставленных разрешений и обрабатывать ситуации, когда разрешение теряется.
Запись данных
Один из распространённых рабочих процессов в Health Connect — запись данных. Для добавления записей используйте insertRecords
.
В следующем примере показано, как записывать данные, вставляя количество шагов:
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
}
}
Обновление данных
Если вам нужно изменить одну или несколько записей, особенно если вам нужно синхронизировать хранилище данных приложения с данными Health Connect, вы можете обновить данные. Существует два способа обновления существующих данных, в зависимости от идентификатора, используемого для поиска записей.
Метаданные
Сначала стоит изучить класс Metadata
, поскольку он необходим при обновлении данных. При создании каждая Record
в Health Connect имеет поле metadata
. Следующие свойства важны для синхронизации:
Характеристики | Описание |
---|---|
id | Каждая Record в Health Connect имеет уникальное значение id .Health Connect автоматически заполняет это поле при добавлении новой записи. |
lastModifiedTime | Каждая Record также отслеживает время последнего изменения записи.Health Connect заполняет эту информацию автоматически. |
clientRecordId | Каждая Record может иметь связанный с ней уникальный идентификатор, который будет использоваться в качестве ссылки в хранилище данных вашего приложения.Ваше приложение предоставляет это значение. |
clientRecordVersion | Если запись имеет clientRecordId , clientRecordVersion можно использовать для синхронизации данных с версией в хранилище данных вашего приложения.Ваше приложение предоставляет это значение. |
Обновление после прочтения по временному диапазону
Чтобы обновить данные, сначала подготовьте необходимые записи. При необходимости внесите в записи изменения. Затем вызовите updateRecords
для внесения изменений.
В следующем примере показано, как обновить данные. Для этого значения смещения зоны каждой записи приводятся в соответствие с тихоокеанским стандартным временем (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
}
}
Вставить через идентификатор записи клиента
Если вы используете необязательные значения идентификатора клиентской записи и версии клиентской записи, мы рекомендуем использовать insertRecords
вместо updateRecords
.
Функция insertRecords
позволяет обновить и вставить данные. Если данные существуют в Health Connect, исходя из заданного набора идентификаторов клиентских записей, они перезаписываются. В противном случае они записываются как новые. Этот сценарий полезен, когда вам нужно синхронизировать данные из хранилища данных вашего приложения с Health Connect.
В следующем примере показано, как выполнить обновление и вставку данных, извлеченных из хранилища данных приложения:
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
}
}
После этого вы можете вызывать эти функции в основном потоке.
upsertSteps(healthConnectClient, pullStepsFromDatastore())
Проверка значения в версии клиентской записи
Если процесс обновления данных включает версию записи клиента, Health Connect выполняет сравнение значений clientRecordVersion
. Если версия вставленных данных выше версии существующих данных, происходит обновление. В противном случае процесс игнорирует изменение, и значение остаётся прежним.
Чтобы включить версионирование данных, необходимо предоставить Metadata.clientRecordVersion
Long
значение, соответствующее вашей логике версионирования.
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)
)
)
Обновления и вставки не увеличивают version
автоматически при каждом изменении, предотвращая непредвиденные случаи перезаписи данных. Поэтому вам придётся вручную указать более высокое значение.
Лучшие практики записи данных
Приложения должны записывать в Health Connect только данные из собственных источников .
Если данные в вашем приложении были импортированы из другого приложения, то ответственность за запись собственных данных в Health Connect ложится на другое приложение.
Также рекомендуется реализовать логику обработки исключений при записи, таких как выход данных за пределы диапазона или внутренняя системная ошибка. Вы можете применить стратегии отсрочки и повтора к механизму планирования заданий. Если запись в Health Connect в конечном итоге не удалась, убедитесь, что ваше приложение может продолжить работу после этой точки экспорта. Не забывайте регистрировать и сообщать об ошибках для облегчения диагностики.
При отслеживании данных есть несколько рекомендаций, которым вы можете следовать в зависимости от того, как ваше приложение записывает данные.
Обработка часовых поясов
При создании записей, основанных на времени, избегайте установки смещения в zoneOffset.UTC по умолчанию, так как это может привести к неточным временным меткам, когда пользователи находятся в других часовых поясах. Вместо этого рассчитывайте смещение на основе фактического местоположения устройства. Часовой пояс устройства можно получить с помощью 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
)
Более подробную информацию смотрите в документации по ZoneId
.
Пассивное отслеживание
К ним относятся приложения, которые выполняют пассивное отслеживание фитнеса или состояния здоровья, например, непрерывно регистрируют шаги или частоту сердечных сокращений в фоновом режиме.
Вашему приложению необходимо периодически записывать данные в Health Connect следующими способами:
- При каждой синхронизации записывайте только новые данные и обновленные данные, которые были изменены с момента последней синхронизации.
- Запросы на фрагменты данных содержат не более 1000 записей на один запрос на запись.
- Используйте
WorkManager
для планирования периодических фоновых задач с периодом времени не менее 15 минут. - Ограничьте запуск задач только тогда, когда устройство находится в режиме ожидания и уровень заряда батареи достаточно высок.
val constraints = Constraints.Builder()
.requiresBatteryNotLow()
.requiresDeviceIdle(true)
.build()
val writeDataWork = PeriodicWorkRequestBuilder<WriteDataToHealthConnectWorker>(
15,
TimeUnit.MINUTES,
5,
TimeUnit.MINUTES
)
.setConstraints(constraints)
.build()
Активное отслеживание
К ним относятся приложения, отслеживающие действия на основе событий, например, физические упражнения и сон, а также ручное управление данными, например, питанием. Эти записи создаются, когда приложение находится в активном режиме или в редких случаях, когда оно используется несколько раз в день.
Убедитесь, что ваше приложение не заставляет Health Connect работать на протяжении всего мероприятия.
Данные должны быть записаны в Health Connect одним из двух способов:
- Синхронизируйте данные с Health Connect после завершения события. Например, синхронизируйте данные, когда пользователь завершает отслеживаемую тренировку.
- Запланируйте одноразовую задачу с помощью
WorkManager
, чтобы синхронизировать данные позже.
Лучшие практики для детализации и частоты записей
При записи данных в Health Connect используйте подходящее разрешение. Правильное разрешение помогает снизить нагрузку на хранилище, сохраняя при этом согласованность и точность данных. Разрешение данных включает в себя два аспекта:
- Частота записи : как часто ваше приложение отправляет новые данные в Health Connect. Например, записывайте новые данные каждые 15 минут.
- Детализация записанных данных : частота выборки данных. Например, запись данных пульса каждые 5 секунд. Не для всех типов данных требуется одинаковая частота выборки. Обновлять данные о количестве шагов каждую секунду не так выгодно, как обновлять их реже, например, каждые 60 секунд. Однако более высокая частота выборки может предоставить пользователям более детальный и детальный обзор данных о здоровье и физической форме. Частота выборки должна обеспечивать баланс между детализацией и производительностью.
Запись данных, отслеживаемых в течение дня
Для данных, собираемых на постоянной основе, например, количества шагов, ваше приложение должно отправлять записи в Health Connect не реже, чем каждые 15 минут в течение дня.
Тип данных | Единица | Ожидал | Пример |
Шаги | шаги | Каждую минуту | 23:14 - 23:15 - 5 шагов 23:16 - 23:17 - 22 шага 23:17 - 23:18 - 8 шагов |
ШагиКаденция | шагов/мин | Каждую минуту | 23:14 - 23:15 - 5 минут в минуту 23:16 - 23:17 - 22 минуты в минуту 23:17 - 23:18 - 8 минут в минуту |
Толкание инвалидной коляски | толчки | Каждую минуту | 23:14 - 23:15 - 5 толчков 23:16 - 23:17 - 22 толчка 23:17 - 23:18 - 8 толчков |
АктивныеСожженныеКалории | Калории | Каждые 15 минут | 23:15 - 23:30 - 2 калории 23:30 - 23:45 - 25 калорий 23:45 - 00:00 - 5 калорий |
Общее количество сожженных калорий | Калории | Каждые 15 минут | 23:15 - 23:30 - 16 калорий 23:30 - 23:45 - 16 калорий 23:45 - 00:00 - 16 калорий |
Расстояние | км/мин | Каждую минуту | 23:14-23:15 - 0,008 км 23:16 - 23:16 - 0,021 км 23:17 - 23:18 - 0,012 км |
Набранная высота | м | Каждую минуту | 20:36 - 20:37 - 3.048м 20:39 - 20:40 - 3.048м 23:23 - 23:24 - 9.144м |
Пройденные этажи | полы | Каждую минуту | 23:14 - 23:15 - 5 этажей 23:16 - 23:16 - 22 этажа 23:17 - 23:18 - 8 этажей |
Частота сердечных сокращений | ударов в минуту | 4 раза в минуту | 6:11:15 утра - 55 ударов в минуту 6:11:30 утра - 56 ударов в минуту 6:11:45 утра - 56 ударов в минуту 6:12:00 утра - 55 ударов в минуту |
Вариабельность сердечного ритма (Rmssd) | РС | Каждую минуту | 6:11 утра - 23 мс |
Частота дыхания | вдохов/минуту | Каждую минуту | 23:14 - 23:15 - 60 вдохов/минуту 23:16 - 23:16 - 62 вдоха/минуту 23:17 - 23:18 - 64 вдоха/минуту |
Насыщение кислородом | % | Каждый час | 6:11 - 95,208% |
Писать сеансы
Данные следует записывать в Health Connect по окончании тренировки или сеанса сна.
Как минимум, ваша заявка должна соответствовать рекомендациям, указанным в столбце «Ожидаемое » в следующей таблице. По возможности следуйте наиболее оптимальным рекомендациям.
Сеансы упражнений и сна
В следующем примере показано, как записывать данные во время упражнения:
Тип данных | Единица | Ожидал | Лучший | Пример |
Шаги | шаги | Каждую минуту | Каждую секунду | 23:14-23:15 - 5 шагов 23:16 - 23:17 - 22 шага 23:17 - 23:18 - 8 шагов |
ШагиКаденция | шагов/мин | Каждую минуту | Каждую секунду | 23:14-23:15 - 35 ударов в минуту 23:16 - 23:17 - 37 ударов в минуту 23:17 - 23:18 - 40 ударов в минуту |
Толкание инвалидной коляски | толчки | Каждую минуту | Каждую секунду | 23:14-23:15 - 5 толчков 23:16 - 23:17 - 22 толчка 23:17 - 23:18 - 8 толчков |
ВелоспортПедалированиеКаденция | об/мин | Каждую минуту | Каждую секунду | 23:14-23:15 - 65 об/мин 23:16 - 23:17 - 70 об/мин 23:17 - 23:18 - 68 об/мин |
Власть | ватты | Каждую минуту | Каждую секунду | 23:14-23:15 - 250 Вт 23:16 - 23:17 - 255 Вт 23:17 - 23:18 - 245 Вт |
Скорость | км/мин | Каждую минуту | Каждую секунду | 23:14-23:15 - 0,3 км/мин 23:16 - 23:17 - 0,4 км/мин 23:17 - 23:18 -0,4 км/мин |
Расстояние | км/м | Каждую минуту | Каждую секунду | 23:14-23:15 - 0,008 км 23:16 - 23:16 - 0,021 км 23:17 - 23:18 - 0,012 км |
АктивныеСожженныеКалории | Калории | Каждую минуту | Каждую секунду | 23:14-23:15 - 20 калорий 23:16 - 23:17 - 20 калорий 23:17 - 23:18 - 25 калорий |
Общее количество сожженных калорий | Калории | Каждую минуту | Каждую секунду | 23:14-23:15 - 36 калорий 23:16 - 23:17 - 36 калорий 23:17 - 23:18 - 41 калория |
Набранная высота | м | Каждую минуту | Каждую секунду | 20:36 - 20:37 - 3.048м 20:39 - 20:40 - 3.048м 23:23 - 23:24 - 9.144м |
Маршруты тренировок | широта/долгота/высота | Каждые 3-5 секунд | Каждую секунду | |
Частота сердечных сокращений | ударов в минуту | 4 раза в минуту | Каждую секунду | 23:14-23:15 - 150 ударов в минуту |
В следующем примере показано, как записывать данные во время или после сеанса сна:
Тип данных | Единица | Ожидаемые образцы | Пример |
Стадирование сна | этап | Детализированный период времени для каждой стадии сна | 23:46 - 23:50 - бодрствование 23:50 - 23:56 - легкий сон 23:56 - 00:16 - глубокий сон |
Частота сердечных сокращений в состоянии покоя | ударов в минуту | Одно дневное значение (ожидается первым делом утром) | 6:11 утра - 60 ударов в минуту |
Насыщение кислородом | % | Одно дневное значение (ожидается первым делом утром) | 6:11 - 95,208% |
Мультиспортивные мероприятия
Этот подход использует существующие типы и структуры данных, а также проверяет совместимость с текущими реализациями Health Connect и считывателями данных. Этот подход широко применяется на фитнес-платформах.
В следующем примере показано, как записать данные для триатлона:
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))