В приложении "Здоровье и спорт" есть тип данных шаги, который позволяет записывать количество шагов с помощью StepsRecord. Шаги – это основной показатель, который используется для отслеживания физической активности.
Чтение данных о шагах на мобильном устройстве
В Android 14 (уровень API 34) и SDK Extension версии 20 или более поздней в приложении "Здоровье и спорт" есть функция подсчета шагов на устройстве. Если приложению предоставлено разрешение READ_STEPS, сервис "Здоровье и спорт" начинает собирать данные о шагах с устройства Android, и пользователи видят, как эти данные автоматически добавляются в записи Шаги.
Чтобы проверить, доступен ли на устройстве подсчет шагов, убедитесь, что на нем установлена ОС Android 14 (уровень API 34) и как минимум версия 20 расширения SDK:
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 года шаги, отслеживаемые Health Connect, будут приписываться синтетическому названию пакета (SPN), например com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.
Ранее встроенные шаги были связаны с названием пакета android.
В статистике, собранной до июня 2026 г., сохранится название пакета android.
Имена субъектов-служб относятся к определенному устройству и приложению, чтобы защитить конфиденциальность пользователей:
- Стабильный. SPN текущего устройства стабилен для вашего приложения.
- На уровне приложения. Разные приложения на одном устройстве видят разные SPN для данных о шагах на устройстве.
Как узнать количество шагов на устройстве
Поскольку имена субъектов-служб относятся к определенным устройствам и областям, нельзя жестко задавать их значения. Вместо этого используйте API getCurrentDeviceDataSource, чтобы получить имя субъекта-службы для текущего устройства.
Для подсчета шагов на устройстве требуется расширение SDK версии 20 или выше, а API getCurrentDeviceDataSource() доступен в Android 14 (уровень API 34) с расширением SDK версии 22 или выше. Чтобы использовать этот API, задайте для параметра compileSdkExtension значение 22 или выше в файле build.gradle.kts или build.gradle на уровне модуля:
Kotlin
android { compileSdk = 35 compileSdkExtension = 22 }
Яркий
android { compileSdk 35 compileSdkExtension 22 }
API getCurrentDeviceDataSource() пока недоступен в библиотеке Health Connect 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, чтобы определить исходное устройство для отдельных записей. Для шагов на устройстве, которые в агрегированных данных определяются по имени поставщика услуг, можно использовать метаданные устройства, например 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" // Or get from HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME 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 */ }