Theo dõi số bước

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 của 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 cách sử dụng aggregate và không lọc theo DataOrigin, thì các 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ữ nguyên 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ừ 11 trở lên.

API getCurrentDeviceDataSource() hiện chưa có trong thư viện Health Connect Jetpack. Thay vào đó, 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 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 đó 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ề 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.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ị

  • 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, nên rất lý tưởng để theo dõi 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 android trong DataOrigin. Sau ngày này, các yêu cầu sẽ được gán 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 bước trên thiết bị chỉ hoạt động khi có í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 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 từ 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 các 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 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.

// Create a set of permissions for required data types
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.

// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()

val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
  if (granted.containsAll(PERMISSIONS)) {
    // Permissions successfully granted
  } else {
    // Lack of required permissions
  }
}

suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
  val granted = healthConnectClient.permissionController.getGrantedPermissions()
  if (granted.containsAll(PERMISSIONS)) {
    // Permissions already granted; proceed with inserting or reading data
  } else {
    requestPermissions.launch(PERMISSIONS)
  }
}

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 trong bản ghi Bước

Mỗi StepsRecord đều chứa những thông tin sau:

  • count: Số bước đã đi trong khoảng thời gian, dưới dạng Long.
  • startTime: Thời gian bắt đầu của khoảng đo.
  • endTime: Thời gian kết thúc của khoảng thời gian đo.
  • startZoneOffset: Giá trị chênh lệch múi giờ của thời gian bắt đầu.
  • endZoneOffset: Giá trị 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ợ

Bạn có thể sử dụng các giá trị tổng hợp sau đây cho StepsRecord:

Bạn có thể sử dụng các giá trị tổng hợp sau đây 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(
        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 biết 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à thời gian kết thúc:

val response = healthConnectClient.readRecords(
    ReadRecordsRequest(
        StepsRecord::class,
        timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
    )
)
response.records.forEach { record ->
    /* Process records */
}