Theo dõi số bước

Health Connect cung cấp kiểu dữ liệu số bước để ghi lại số bước bằng StepsRecord. Số bước là một chỉ số cơ bản trong việc theo dõi sức khoẻ và thể chất.

Đọc 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 số bước trên thiết bị. Nếu bất kỳ ứng dụng nào được cấp quyền READ_STEPS, Health Connect sẽ bắt đầu thu thập 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 số bước trên thiết bị có được cung cấp 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 tiện ích SDK phiên bản 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 thay đổi gì cho bản cập nhật tháng 6 năm 2026.

Thay đổi phân bổ cho số bước trên thiết bị

Kể từ bản cập nhật tháng 6 năm 2026, số bước được Health Connect theo dõi tự nhiên sẽ được phân bổ cho Tên gói tổng hợp (SPN), chẳng hạn như com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.

Trước đây, số bước tích hợp được phân bổ cho tên gói android. Dữ liệu về số bước trong quá khứ đượ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 phân bổ 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ân bổ theo ứ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 về số bước trên thiết bị.

Truy vấn số bước trên thiết bị

Vì SPN được phân bổ 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 số bước trên thiết bị yêu cầu tiện ích SDK phiên bản 20 trở lên, nhưng API getCurrentDeviceDataSource() có trên Android 14 (cấp độ API 34) với tiện ích SDK phiên bản 11 trở lên.

API getCurrentDeviceDataSource() hiện chưa có trong thư viện Health Connect Jetpack. Các ví dụ sau đây sử dụng API khung Android:

import android.content.Context
import android.health.connect.HealthConnectManager

val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
val deviceDataSource = healthConnectManager?.getCurrentDeviceDataSource()
val currentDeviceSpn = deviceDataSource?.deviceDataOrigin?.packageName

Nếu ứng dụng của bạn cần đọc số bước trên thiết bị hoặc nếu ứng dụng đó hiển thị dữ liệu về số bước được chia 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 đó DataOriginandroid 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ề số bước, hãy sử dụng metadata.device để xác định thiết bị nguồn cho từng bản ghi. Đối với số bước trên thiết bị được xác định bằng SPN 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 số bước trên thiết bị.

Ví dụ sau đây cho thấy 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.HealthConnectManager
import android.os.Build
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

suspend fun readDeviceStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    context: Context,
    startTime: Instant,
    endTime: Instant
) {
    // 1. Check if SDK Extension 11+ is available for getCurrentDeviceDataSource()
    val isDataSourceApiAvailable = Build.VERSION.SDK_INT >= Build.VERSION_CODES.U &&
            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.U) >= 11

    try {
        val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)

        // 2. Safely fetch the package name only if API is available and data exists
        val currentDeviceSpn = if (isDataSourceApiAvailable) {
            healthConnectManager?.getCurrentDeviceDataSource()?.deviceDataOrigin?.packageName
        } 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ị

  • Mức sử dụng cảm biến: Health Connect sử dụng cảm biến TYPE_STEP_COUNTER từ SensorManager. Cảm biến này được tối ưu hoá để tiêu thụ ít điện năng, giúp cảm biến này trở nên lý tưởng cho việc theo dõi số bước liên tục ở chế độ nền.
  • Mức độ chi tiết của dữ liệu: Để tiết kiệm pin, dữ liệu về số bước 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 một lần mỗi phút.
  • Phân bổ: Số bước được ghi lại bằng tính năng này trước tháng 6 năm 2026 được phân bổ cho tên gói android trong DataOrigin. Sau ngày này, số bước đó sẽ được phân bổ cho SPN dành riêng cho thiết bị. Hãy xem bài viết Thay đổi phân bổ cho số 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_STEPS trong Health Connect.

Kiểm tra phạm vi sử dụng 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 tất cả thiết bị hoặc có thể bị tắt. Bạn có thể kiểm tra phạm vi sử dụng 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 do 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 số bước được bảo vệ bằng các quyền sau:

  • android.permission.health.READ_STEPS
  • android.permission.health.WRITE_STEPS

Để thêm khả năng số bước vào ứng dụng của bạn, 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 số bước:

<application>
  <uses-permission
android:name="android.permission.health.WRITE_STEPS" />
...
</application>

Để đọc số 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)
    )
Hãy sử dụng 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.") }
    }
}
Vì người dùng có thể cấp hoặc thu hồi quyền bất cứ lúc nào, nên ứng dụng của bạn cần kiểm tra quyền mỗi khi sử dụng và xử lý các trường hợp mất quyền.

Thông tin có trong bản ghi Số bước

Mỗi StepsRecord chứa các thông tin sau:

  • count: Số bước trong khoảng thời gian, ở dạng Long.
  • 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: Mức chênh lệch múi giờ cho thời gian bắt đầu.
  • endZoneOffset: Mức 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ợ

Các giá trị tổng hợp sau đây có cho StepsRecord:

Các giá trị tổng hợp sau đây có cho StepsCadenceRecord:

Ví dụ về cách sử dụng

Các phần sau đây cho thấy 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 về số bước bằng cách chèn StepsRecord thực thể. 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(
        device = Device(type = Device.TYPE_WATCH),
        recordingMethod = Metadata.RECORDING_METHOD_AUTOMATICALLY_RECORDED
    )
)
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 cho thấy cách đọc tổng số bước của một người dùng trong một khoảng 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 thô StepsRecord 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 */
}