На платформе "Здоровье и спорт" есть тип данных Шаги, который позволяет записывать количество шагов с помощью StepsRecord. Шаги – это фундаментальный показатель в отслеживании здоровья и физической активности.
Это руководство совместимо с платформой "Здоровье и спорт" версии 1.1.0-alpha12 или более поздней.
Прочитать шаги на мобильном устройстве
Начиная с Android 14 (уровень API 34) и SDK Extension версии 20, платформа "Здоровье и спорт" поддерживает подсчет шагов на устройстве. Если какому-либо приложению предоставлено разрешение READ_STEPS, сервис "Здоровье и спорт" начинает собирать данные о шагах с устройства Android, и пользователи видят, что эти данные автоматически добавляются в записи Шаги.
Чтобы узнать, доступен ли подсчет шагов на устройстве, убедитесь, что на нем установлена ОС Android 14 (уровень API 34) и расширение SDK версии 20 или более поздней.
val isStepTrackingAvailable =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20
Если ваше приложение считывает агрегированное количество шагов с помощью aggregate и не фильтрует по DataOrigin, шаги, подсчитанные на устройстве, автоматически включаются в общее количество. В этом случае никаких изменений в связи с обновлением в июне 2026 года не требуется.
Изменение атрибуции для шагов, подсчитанных на устройстве
Начиная с обновления за июнь 2026 года, шаги, отслеживаемые непосредственно платформой "Здоровье и спорт", связываются с синтетическим названием пакета (SPN), например com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.
Ранее встроенные шаги относились к пакету android.
Для статистики шагов, записанной до июня 2026 года, сохраняется название пакета android.
Чтобы защитить конфиденциальность пользователей, SPN-номера уникальны для каждого устройства и приложения.
- Стабильный. SPN текущего устройства стабилен для вашего приложения.
- На уровне приложения. Разные приложения на одном устройстве видят разные SPN для данных о шагах, хранящихся на устройстве.
Запрос для действий на устройстве
Поскольку SPN-имена зависят от области действия и устройства, не следует задавать их в коде. Вместо этого используйте API getCurrentDeviceDataSource, чтобы получить SPN для текущего устройства.
Для подсчета шагов на устройстве требуется SDK Extension версии 20 или выше, а API getCurrentDeviceDataSource() доступен на Android 14 (уровень API 34) с SDK Extension версии 22 или выше. Чтобы использовать этот API, задайте для параметра compileSdkExtension значение 22 или выше в файле build.gradle.kts или build.gradle на уровне модуля:
Kotlin
android { compileSdk = 35 compileSdkExtension = 22 }
Яркий
android { compileSdk 35 compileSdkExtension 22 }
API getCurrentDeviceDataSource() пока недоступен в библиотеке Jetpack приложения "Здоровье и спорт". В примерах ниже используется API фреймворка Android, для которого требуются обратные вызовы Executor и OutcomeReceiver.
import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.OutcomeReceiver
val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
healthConnectManager?.getCurrentDeviceDataSource(
context.mainExecutor,
object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
override fun onResult(result: DeviceDataSource) {
val currentDeviceSpn = result.deviceDataOrigin.packageName
}
override fun onError(error: HealthConnectException) {
// Handle error
}
}
)
Если вашему приложению нужно считывать данные о шагах с устройства или показывать их с разбивкой по исходному приложению или устройству, запросите записи, в которых DataOrigin – это android или соответствует SPN устройства. Если в вашем приложении показывается атрибуция для данных о шагах, используйте metadata.device, чтобы определить исходное устройство для отдельных записей. Для шагов на устройстве, идентифицированных по SPN в агрегированных данных, вы можете использовать метаданные устройства, например model или manufacturer из DeviceDataSource, или общую метку, например "Ваш телефон".
В следующем примере показано, как считать агрегированные данные о количестве шагов на устройстве, отфильтровав их по android и SPN текущего устройства:
import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.metadata.DataOrigin
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.time.TimeRangeFilter
import java.time.Instant
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine
suspend fun readDeviceStepsByTimeRange(
healthConnectClient: HealthConnectClient,
context: Context,
startTime: Instant,
endTime: Instant
) {
// 1. Check if SDK Extension 22+ is available for getCurrentDeviceDataSource()
val isDataSourceApiAvailable =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 22
try {
val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
// 2. Safely fetch the package name only if the API is available
val currentDeviceSpn = if (isDataSourceApiAvailable && healthConnectManager != null) {
suspendCancellableCoroutine { continuation ->
healthConnectManager.getCurrentDeviceDataSource(
context.mainExecutor,
object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
override fun onResult(result: DeviceDataSource) {
continuation.resume(result.deviceDataOrigin.packageName)
}
override fun onError(error: HealthConnectException) {
continuation.resumeWithException(error)
}
}
)
}
} else {
null
}
val dataOriginFilters = mutableSetOf(DataOrigin("android"))
// 3. Explicit null-safety check using .let
currentDeviceSpn?.let {
dataOriginFilters.add(DataOrigin(it))
}
val response = healthConnectClient.aggregate(
AggregateRequest(
metrics = setOf(StepsRecord.COUNT_TOTAL),
timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
dataOriginFilter = dataOriginFilters
)
)
val stepCount = response[StepsRecord.COUNT_TOTAL]
} catch (e: Exception) {
// Now this catch block only handles actual runtime exceptions,
// rather than Errors from missing methods.
}
}
Подсчет шагов на устройстве
- Использование датчиков. Платформа "Здоровье и спорт" использует датчик
TYPE_STEP_COUNTERиз приложения "SensorManager". Этот датчик оптимизирован для низкого энергопотребления, что делает его идеальным для непрерывного фонового отслеживания шагов. - Детализация данных. Чтобы экономить заряд батареи, данные о шагах обычно записываются в базу данных платформы "Здоровье и спорт" не чаще одного раза в минуту.
- Атрибуция. Шаги, записанные этой функцией до июня 2026 года, относятся к названию пакета
androidвDataOrigin. После этой даты они будут относиться к SPN для конкретного устройства. Подробнее об изменении атрибуции шагов, подсчитанных на устройстве… - Активация. Механизм подсчета шагов на устройстве активен, только если хотя бы одному приложению на устройстве предоставлено разрешение
READ_STEPSв сервисе "Здоровье и спорт".
Как проверить доступность платформы "Здоровье и спорт"
Прежде чем использовать платформу "Здоровье и спорт", приложение должно проверить, доступна ли она на устройстве пользователя. Платформа "Здоровье и спорт" может быть не предустановлена на некоторых устройствах или отключена.
Проверить доступность можно с помощью метода HealthConnectClient.getSdkStatus().
Как проверить доступность платформы "Здоровье и спорт"
fun checkHealthConnectAvailability(context: Context) { val providerPackageName = "com.google.android.apps.healthdata" val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName) if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) { // Health Connect is not available. Guide the user to install/enable it. // For example, show a dialog. return // early return as there is no viable integration } if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) { // Health Connect is available but requires an update. // Optionally redirect to package installer to find a provider, for example: val uriString = "market://details?id=$providerPackageName&url=healthconnect%3A%2F%2Fonboarding" context.startActivity( Intent(Intent.ACTION_VIEW).apply { setPackage("com.android.vending") data = Uri.parse(uriString) putExtra("overlay", true) putExtra("callerId", context.packageName) } ) return } // Health Connect is available, obtain a HealthConnectClient instance val healthConnectClient = HealthConnectClient.getOrCreate(context) // Issue operations with healthConnectClient }
В зависимости от статуса, возвращенного getSdkStatus(), вы можете при необходимости предложить пользователю установить или обновить приложение "Здоровье и спорт" из Google Play.
Требуемые права доступа
Доступ к шагам защищен следующими разрешениями:
android.permission.health.READ_STEPSandroid.permission.health.WRITE_STEPS
Чтобы добавить в приложение возможность подсчета шагов, запросите разрешения для типа данных Steps.
Вот разрешение, которое нужно объявить, чтобы записывать данные о шагах:
<application>
<uses-permission
android:name="android.permission.health.WRITE_STEPS" />
...
</application>
Чтобы читать данные о шагах, запросите следующие разрешения:
<application>
<uses-permission
android:name="android.permission.health.READ_STEPS" />
...
</application>
Запросите разрешения у пользователя
После создания экземпляра клиента приложение должно запросить разрешения у пользователя. У пользователей должна быть возможность в любой момент предоставить или отозвать разрешения. Для этого создайте набор разрешений для нужных типов данных. Убедитесь, что разрешения в наборе сначала объявлены в манифесте Android.
val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class) )
getGrantedPermissions, чтобы проверить, есть ли у приложения необходимые разрешения. Если нет, запросите их с помощью createRequestPermissionResultContract. Откроется экран разрешений платформы "Здоровье и спорт".
val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class), HealthPermission.getReadPermission(HeartRateRecord::class), HealthPermission.getWritePermission(HeartRateRecord::class) ) val requestPermissionsLauncher = rememberLauncherForActivityResult( contract = PermissionController.createRequestPermissionResultContract() ) { grantedPermissions -> if (grantedPermissions.containsAll(permissions)) { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") } } else { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") } } }
Информация, которая содержится в записи о шагах
Каждый StepsRecord содержит следующую информацию:
count– количество шагов, сделанных за определенный интервал времени. Тип данных –Long.startTime– время начала интервала измерения.endTime– время окончания интервала измерения.startZoneOffset– смещение часового пояса для времени начала.endZoneOffset– смещение часового пояса для времени окончания.
Поддерживаемые агрегирования
Для StepsRecord доступны следующие агрегированные значения:
Для StepsCadenceRecord доступны следующие агрегированные значения:
Примеры использования
В следующих разделах показано, как читать и записывать данные StepsRecord.
Запись данных о шагах
Приложение может записывать данные о количестве шагов, вставляя экземпляры StepsRecord. В примере ниже показано, как записать 1000 шагов, сделанных пользователем.
val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime) val stepsRecord = StepsRecord( count = 120, startTime = startTime, endTime = endTime, startZoneOffset = zoneOffset, endZoneOffset = zoneOffset, metadata = Metadata.autoRecorded( device = Device(type = Device.TYPE_WATCH) ) ) healthConnectClient.insertRecords(listOf(stepsRecord))
Чтение агрегированных данных
Самый распространенный способ чтения данных о шагах – агрегирование общего количества шагов за определенный период времени. В примере ниже показано, как получить общее количество шагов пользователя за определенный интервал времени.
suspend fun readStepsAggregate(startTime: Instant, endTime: Instant): Long { val response = healthConnectClient.aggregate( AggregateRequest( metrics = setOf(StepsRecord.COUNT_TOTAL), timeRangeFilter = TimeRangeFilter.between(startTime, endTime) ) ) return response[StepsRecord.COUNT_TOTAL] ?: 0L }
Чтение первичных данных
В примере ниже показано, как считать необработанные данные StepsRecord за определенный период.
val response = healthConnectClient.readRecords( ReadRecordsRequest( StepsRecord::class, timeRangeFilter = TimeRangeFilter.between(startTime, endTime) ) ) response.records.forEach { record -> /* Process records */ }