健康数据共享提供步数数据类型,用于使用 StepsRecord 记录步数。步数是健康和健身追踪的基本测量指标。
读取移动步数
在 Android 14(API 级别 34)和 SDK 扩展版本 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 年 6 月的更新要求。
设备端步数的归因变更
自 2026 年 6 月更新起,由健康数据共享原生跟踪的步数将归因于合成软件包名称 (SPN),例如 com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e。
之前,内置步数归因于软件包名称 android。2026 年 6 月之前记录的历史步数数据会保留 android 软件包名称。
SPN 针对特定设备,并按应用进行范围限定,以保护用户隐私:
- 稳定:当前设备的 SPN 对您的应用来说是稳定的。
- 应用级:同一设备上的不同应用会看到不同的设备端步数数据 SPN。
查询设备端步数
由于 SPN 是有范围且特定于设备的,因此您不得对 SPN 值进行硬编码。而是改用 getCurrentDeviceDataSource API 来检索当前设备的 SPN。
虽然设备端步数统计需要 SDK 扩展版本 20 或更高版本,但 getCurrentDeviceDataSource() API 在 Android 14(API 级别 34)上可用,且需要 SDK 扩展版本 22 或更高版本。如需使用此 API,请在模块级 build.gradle.kts 或 build.gradle 文件中将 compileSdkExtension 设置为 22 或更高版本:
Kotlin
android { compileSdk = 35 compileSdkExtension = 22 }
Groovy
android { compileSdk 35 compileSdkExtension 22 }
getCurrentDeviceDataSource() API 尚未在健康数据共享 Jetpack 库中提供。以下示例使用 Android 框架 API,该 API 需要 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 标识的设备端步骤,您可以使用来自 DeviceDataSource 的 model 或 manufacturer 等设备元数据进行归因,也可以使用“您的手机”等通用标签来表示设备端步骤。
以下示例展示了如何通过同时过滤 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.
}
}
设备端步数统计
- 传感器使用情况:健康数据共享使用
SensorManager的TYPE_STEP_COUNTER传感器。此传感器经过优化,可降低功耗,非常适合在后台持续跟踪步数。 - 数据粒度:为了节省电池电量,步数数据通常会分批写入健康数据共享数据库,写入频率不会超过每分钟一次。
- 归因:此功能在 2026 年 6 月之前记录的步数归因于
DataOrigin中的android软件包名称。在此日期之后,它们会归因于特定于设备的 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 实例来写入步数数据。以下示例展示了如何记录用户行走的 1,000 步:
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 */ }