Health Connect cung cấp loại dữ liệu số bước để ghi lại số bước bằng StepsRecord. Số bước là một chỉ số đo lường cơ bản trong tính năng theo dõi sức khoẻ và hoạt động thể chất.
Đọc dữ liệu về số bước trên thiết bị di động
Với Android 14 (cấp độ API 34) và Tiện ích SDK phiên bản 20 trở lên, Health Connect cung cấp tính năng đếm bước trên thiết bị. Nếu bạn đã cấp quyền READ_STEPS cho bất kỳ ứng dụng nào, Health Connect sẽ bắt đầu ghi lại số bước từ thiết bị chạy Android và người dùng sẽ thấy dữ liệu về số bước được tự động thêm vào các mục Số bước trong Health Connect.
Để kiểm tra xem tính năng đếm bước trên thiết bị có hoạt động hay không, hãy xác minh rằng thiết bị đang chạy Android 14 (cấp độ API 34) và có ít nhất phiên bản tiện ích SDK 20:
val isStepTrackingAvailable =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20
Nếu ứng dụng của bạn đọc số bước tổng hợp bằng aggregate và không lọc theo DataOrigin, thì số bước trên thiết bị sẽ tự động được đưa vào tổng số và bạn không cần thực hiện thay đổi nào cho bản cập nhật tháng 6 năm 2026.
Thay đổi về hoạt động phân bổ cho các bước trên thiết bị
Kể từ bản cập nhật tháng 6 năm 2026, các bước được Health Connect theo dõi một cách tự nhiên sẽ được gán cho một Tên gói tổng hợp (SPN), chẳng hạn như com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.
Trước đây, các bước tích hợp sẵn được gán cho tên gói android.
Dữ liệu về số bước được ghi lại trước tháng 6 năm 2026 vẫn giữ tên gói android.
SPN dành riêng cho thiết bị và được giới hạn phạm vi theo từng ứng dụng để bảo vệ quyền riêng tư của người dùng:
- Ổn định: SPN cho thiết bị hiện tại ổn định đối với ứng dụng của bạn.
- Phạm vi ứng dụng: Các ứng dụng khác nhau trên cùng một thiết bị sẽ thấy các SPN khác nhau cho dữ liệu bước trên thiết bị.
Truy vấn các bước trên thiết bị
Vì SPN có phạm vi và dành riêng cho thiết bị, nên bạn không được mã hoá cứng các giá trị SPN. Thay vào đó, hãy sử dụng API getCurrentDeviceDataSource để truy xuất SPN cho thiết bị hiện tại.
Mặc dù tính năng đếm bước trên thiết bị yêu cầu phiên bản tiện ích SDK từ 20 trở lên, nhưng API getCurrentDeviceDataSource() có trên Android 14 (cấp độ API 34) với phiên bản tiện ích SDK từ 22 trở lên. Để sử dụng API này, hãy đặt compileSdkExtension thành 22 trở lên trong tệp build.gradle.kts hoặc build.gradle ở cấp mô-đun:
Kotlin
android { compileSdk = 35 compileSdkExtension = 22 }
Groovy
android { compileSdk 35 compileSdkExtension 22 }
API getCurrentDeviceDataSource() chưa có trong thư viện Health Connect Jetpack. Các ví dụ sau đây sử dụng API khung Android, yêu cầu một lệnh gọi lại Executor và 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
}
}
)
Nếu ứng dụng của bạn cần đọc các bước trên thiết bị hoặc nếu ứng dụng hiển thị dữ liệu về các bước được chia nhỏ theo ứng dụng hoặc thiết bị nguồn, thì bạn phải truy vấn các bản ghi trong đó DataOrigin là android hoặc khớp với SPN của thiết bị. Nếu ứng dụng của bạn cho thấy thông tin phân bổ cho dữ liệu về bước, hãy dùng metadata.device để xác định thiết bị nguồn cho từng bản ghi. Đối với các bước trên thiết bị do SPN xác định trong dữ liệu tổng hợp, bạn có thể sử dụng siêu dữ liệu thiết bị như model hoặc manufacturer từ DeviceDataSource để phân bổ, hoặc sử dụng nhãn chung như "Điện thoại của bạn" cho các bước trên thiết bị.
Ví dụ sau đây cho biết cách đọc dữ liệu tổng hợp về số bước trên thiết bị bằng cách lọc cả android và SPN của thiết bị hiện tại:
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.
}
}
Đếm số bước trên thiết bị
- Sử dụng cảm biến: Health Connect sử dụng cảm biến
TYPE_STEP_COUNTERtừSensorManager. Cảm biến này được tối ưu hoá để tiêu thụ ít điện năng, nên rất phù hợp để theo dõi số bước liên tục ở chế độ nền. - Độ chi tiết của dữ liệu: Để tiết kiệm pin, dữ liệu về bước đi thường được xử lý theo lô và ghi vào cơ sở dữ liệu Health Connect không thường xuyên hơn 1 lần/phút.
- Phân bổ: Các bước được ghi lại bằng tính năng này trước tháng 6 năm 2026 sẽ được phân bổ cho tên gói
androidtrongDataOrigin. Sau ngày này, các lượt xem sẽ được tính cho một SPN dành riêng cho thiết bị. Xem phần Thay đổi về mô hình phân bổ cho các bước trên thiết bị. - Kích hoạt: Cơ chế đếm số bước trên thiết bị chỉ hoạt động khi ít nhất một ứng dụng trên thiết bị được cấp quyền
READ_STEPStrong Health Connect.
Kiểm tra phạm vi cung cấp của Health Connect
Trước khi cố gắng sử dụng Health Connect, ứng dụng của bạn phải xác minh rằng Health Connect có trên thiết bị của người dùng. Health Connect có thể không được cài đặt sẵn trên một số thiết bị hoặc có thể bị tắt.
Bạn có thể kiểm tra phạm vi cung cấp bằng phương thức HealthConnectClient.getSdkStatus().
Cách kiểm tra phạm vi sử dụng Health Connect
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 }
Tuỳ thuộc vào trạng thái mà getSdkStatus() trả về, bạn có thể hướng dẫn người dùng cài đặt hoặc cập nhật Health Connect trên Cửa hàng Google Play nếu cần.
Các quyền bắt buộc
Quyền truy cập vào dữ liệu về số bước được bảo vệ bằng các quyền sau:
android.permission.health.READ_STEPSandroid.permission.health.WRITE_STEPS
Để thêm chức năng bước vào ứng dụng, hãy bắt đầu bằng cách yêu cầu quyền cho kiểu dữ liệu Steps.
Dưới đây là quyền bạn cần khai báo để có thể ghi các bước:
<application>
<uses-permission
android:name="android.permission.health.WRITE_STEPS" />
...
</application>
Để đọc các bước, bạn cần yêu cầu các quyền sau:
<application>
<uses-permission
android:name="android.permission.health.READ_STEPS" />
...
</application>
Yêu cầu người dùng cấp quyền
Sau khi tạo một phiên bản ứng dụng, ứng dụng của bạn cần yêu cầu người dùng cấp quyền. Người dùng phải được phép cấp hoặc từ chối cấp quyền bất cứ lúc nào. Để thực hiện việc này, hãy tạo một tập hợp quyền cho các kiểu dữ liệu bắt buộc. Trước tiên, bạn cần khai báo các quyền trong tập hợp này ở tệp kê khai Android.
val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class) )
getGrantedPermissions để xem ứng dụng của bạn đã được cấp các quyền cần thiết chưa. Nếu chưa, hãy sử dụng createRequestPermissionResultContract để yêu cầu các quyền đó. Thao tác này sẽ hiện màn hình các quyền của Health Connect.
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.") } } }
Thông tin có trong bản ghi Bước
Mỗi StepsRecord chứa những thông tin sau:
count: Số bước đã thực hiện trong khoảng thời gian, dưới dạngLong.startTime: Thời gian bắt đầu của khoảng thời gian đo.endTime: Thời gian kết thúc của khoảng thời gian đo.startZoneOffset: Độ lệch múi giờ của thời gian bắt đầu.endZoneOffset: Chênh lệch múi giờ cho thời gian kết thúc.
Các phép tổng hợp được hỗ trợ
Sau đây là các giá trị tổng hợp có sẵn cho StepsRecord:
Sau đây là các giá trị tổng hợp có sẵn cho StepsCadenceRecord:
Ví dụ về cách sử dụng
Các phần sau đây cho biết cách đọc và ghi dữ liệu StepsRecord.
Ghi dữ liệu về số bước
Ứng dụng của bạn có thể ghi dữ liệu số bước bằng cách chèn các thực thể StepsRecord. Ví dụ sau đây cho thấy cách ghi lại 1.000 bước mà người dùng đã thực hiện:
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))
Đọc dữ liệu tổng hợp
Cách phổ biến nhất để đọc dữ liệu về số bước là tổng hợp tổng số bước trong một khoảng thời gian. Ví dụ sau đây minh hoạ cách đọc tổng số bước của một người dùng trong một phạm vi thời gian nhất định:
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 }
Đọc dữ liệu thô
Ví dụ sau đây cho thấy cách đọc dữ liệu StepsRecord thô trong khoảng thời gian bắt đầu và kết thúc:
val response = healthConnectClient.readRecords( ReadRecordsRequest( StepsRecord::class, timeRangeFilter = TimeRangeFilter.between(startTime, endTime) ) ) response.records.forEach { record -> /* Process records */ }