Health Connect в Android в 2026 году: как читать и писать данные, не отказываясь от local-first
Практическое руководство по API Health Connect в Android в 2026 году — разрешения, фоновое чтение и почему local-first-приложениям стоит относиться к нему как к опциональному источнику, а не как к истине.
Health Connect — это хранилище на самом устройстве, из которого Android теперь ожидает, что приложения для здоровья и фитнеса будут читать и в которое будут писать данные, вместо того чтобы держать собственный изолированный склад. Для local-first-приложения это ставит честный вопрос: это место, куда публикуют данные, место, откуда их читают, или и то и другое сразу — и не переносит ли согласие на любой из этих вариантов источник истины с устройства, которое вы обещали хранить его на себе. Вот что API на самом деле от вас требует, и вот где я провожу границу.
Что такое Health Connect, а что нет
Health Connect — это хранилище данных на уровне системы, а не облачный сервис. Записи — шаги, потребление воды, вес, сеансы сна — живут в локальной базе данных, доступ к которой опосредует Android, и любое приложение, которое пользователь одобрил, может читать или писать через неё. В этом и привлекательность: приложение для отслеживания воды и фитнес-приложение могут делить один общий итог по гидратации вместо того, чтобы каждое вело отдельный, неполный подсчёт. В этом же и риск. В тот момент, когда ваше приложение пишет туда данные, любое другое приложение, которому пользователь предоставил доступ на чтение, тоже может их увидеть. Local-first не перестаёт что-либо значить, как только вы прикасаетесь к Health Connect, но значит он теперь нечто более узкое: данные всё ещё никогда не покидают устройство, но видны они уже не только вам.
На телефонах без предустановленного приложения Health Connect клиентская библиотека при первом обращении предлагает установить его из Play. Заложите время на этот путь отказа — на новом устройстве или урезанной прошивке API может быть недоступен, и запрос разрешения должен иметь настоящий запасной вариант, а не приводить к падению.
Модель разрешений 2026 года
Разрешения на данные о здоровье — это не runtime-разрешения в смысле ActivityCompat.requestPermissions: они проходят через отдельный экран с обоснованием, которым владеет само приложение Health Connect, а не диалог вашего приложения. Вы декларируете намерение в манифесте, а затем запускаете контракт:
val requestPermissions = registerForActivityResult(
PermissionController.createRequestPermissionResultContract()
) { granted ->
if (HealthPermission.getWritePermission(HydrationRecord::class) in granted) {
// proceed
}
}
requestPermissions.launch(
setOf(
HealthPermission.getWritePermission(HydrationRecord::class),
HealthPermission.getReadPermission(HydrationRecord::class),
)
)
Возвращённый набор говорит только о том, что было предоставлено, но никогда — почему в чём-то отказали: нет флага «отказано навсегда», нет обоснования, которое можно было бы посмотреть. Если разрешения, которое вы ожидали, нет в возвращённом наборе, правильный шаг — проверять PermissionController.getGrantedPermissions() перед каждым чтением или записью и корректно деградировать, а не полагаться на то, что вчерашнее согласие всё ещё в силе. Пользователи могут в любой момент отозвать разрешения Health Connect из системных настроек, совершенно независимо от жизненного цикла вашего приложения, и следующий же вызов API просто завершится неудачей, как будто разрешения никогда и не было.
Запись и чтение записи
HydrationRecord — это значение с временным диапазоном и объёмом, привязанное к собственным метаданным пишущего приложения:
val record = HydrationRecord(
startTime = Instant.now(),
startZoneOffset = ZoneOffset.systemDefault().rules.getOffset(Instant.now()),
endTime = Instant.now(),
endZoneOffset = ZoneOffset.systemDefault().rules.getOffset(Instant.now()),
volume = Volume.milliliters(250.0),
)
healthConnectClient.insertRecords(listOf(record))
Чтение — это запрос по временному диапазону, а не сканирование всей таблицы: от вас ожидают запроса окна, а не «всего подряд»:
val response = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = HydrationRecord::class,
timeRangeFilter = TimeRangeFilter.between(
startOfToday, Instant.now()
),
)
)
Каждая запись несёт metadata.dataOrigin, так что можно отличить собственные записи от записей фитнес-трекера или другого приложения — это пригодится в тот момент, когда вы собираете «итог за сегодня» из нескольких источников и не хотите посчитать что-то дважды.
Фоновое чтение требует отдельного разрешения
Чтение данных Health Connect, когда ваше приложение не находится на переднем плане, требует PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND в дополнение к разрешению на конкретный тип записи, и Android считает это достаточно чувствительным, чтобы выделить под него отдельную строку на экране обоснования. Если ваш сценарий — «синхронизироваться один раз при открытии приложения», пропустите это разрешение; запрашивайте его только ради настоящей фоновой задачи, например виджета или ежедневного уведомления со сводкой, которому нужно посчитать число ещё до того, как пользователь откроет приложение. Запрос этого разрешения без реальной необходимости технически ничего не ломает — он просто делает ваш экран разрешений длиннее и тревожнее, чем оправдывает функциональность, а это как раз то, из-за чего вы рискуете потерять согласие целиком.
Почему я отношусь к нему как к опциональным, дополнительным данным
Для такого приложения, как Hydrame, Health Connect соблазнителен по одной причине: пользователь, который также записывает тренировки где-то ещё, получает одно верное число по гидратации вместо двух противоречащих друг другу. Но соблазнителен он и по неверной причине тоже — легко позволить «синхронизации с Health Connect» незаметно превратиться в «Health Connect теперь источник истины», и в этот момент собственная локальная база данных приложения превращается в кеш, который может разойтись с реальностью, а обещание, что ничто не покидает устройство, становится всё более размытым с каждым новым приложением, которому предоставлен доступ на чтение. Версия, которую я на самом деле выпустил бы, использует Health Connect как одностороннюю публикацию той же локальной записи — и никогда как путь чтения, от которого зависит работа приложения: удалите приложение Health Connect целиком, и основной сценарий использования не должен этого заметить.
Чек-лист
Прежде чем подключать Health Connect: проверьте доступность и обработайте случай отсутствующего приложения, запрашивайте только те конкретные разрешения на записи, которые действительно используете, перепроверяйте предоставленные разрешения перед каждым обращением вместо того, чтобы полагаться на согласие, полученное три экрана назад, ограничивайте фоновое чтение реальными фоновыми задачами и заранее решите, публикуете вы данные в Health Connect или зависите от него — потому что API с готовностью позволит и то, и другое, а честным по отношению к тому, где на самом деле живут его данные, local-first-приложение делает только один из этих вариантов.
// По теме
Ещё из журнала
Биометрическая аутентификация в Android в 2026 году: BiometricPrompt, Keystore и блокировка local-first приложения
Практическое руководство по androidx.biometric в Android — BiometricPrompt, ключи на основе CryptoObject, резервный вариант с учётными данными устройства и ошибки, из-за которых проверка отпечатка не защищает ничего.
In-App Review API в Android: как просить оценку, не раздражая пользователя
Практическое руководство по In-App Review API от Google — как он на самом деле работает, когда его запускать и почему обычный попап «оцените нас» незаметно портит вашу оценку в Play Store.
Ярлыки приложений Android и плитка быстрых настроек: логирование воды без открытия приложения
Практическое руководство по динамическому ShortcutManager API и TileService в Android — как позволить действию в один тап полностью обойти приложение, и о подводных камнях, на которых спотыкается большинство реализаций.