Đọc dữ liệu thô

Ví dụ sau đây minh hoạ cách đọc dữ liệu thô trong quy trình công việc chung.

Đọc dữ liệu

Health Connect cho phép các ứng dụng đọc dữ liệu từ kho dữ liệu khi ứng dụng ở nền trước và nền sau:

  • Đọc trên nền trước: Thông thường, bạn có thể đọc dữ liệu từ Health Connect khi ứng dụng ở nền trước. Trong những trường hợp này, bạn có thể cân nhắc sử dụng một dịch vụ trên nền trước để chạy thao tác này trong trường hợp người dùng hoặc hệ thống đặt ứng dụng của bạn ở chế độ nền trong quá trình đọc.

  • Đọc dữ liệu ở chế độ nền: Bằng cách yêu cầu người dùng cấp thêm quyền, bạn có thể đọc dữ liệu sau khi người dùng hoặc hệ thống đặt ứng dụng của bạn ở chế độ nền. Xem ví dụ hoàn chỉnh về hoạt động đọc dữ liệu ở chế độ nền.

Loại dữ liệu về Số bước trong Health Connect ghi lại số bước người dùng đi được giữa các kết quả đọc. Số bước chính là cách đo lường phổ biến trên các nền tảng về sức khoẻ, hoạt động thể chất và sức khoẻ tinh thần. Health Connect cho phép bạn đọc và ghi dữ liệu về số bước đi.

Để đọc bản ghi, hãy tạo một ReadRecordsRequest và cung cấp bản ghi đó khi bạn gọi readRecords.

Ví dụ sau đây minh hoạ cách đọc dữ liệu về số bước của một người dùng trong một khoảng thời gian nhất định. Để xem ví dụ mở rộng về SensorManager, hãy xem hướng dẫn về dữ liệu số bước.

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

Bạn cũng có thể đọc dữ liệu theo cách tổng hợp bằng hàm aggregate.

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 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.

Ví dụ về hoạt động đọc ở chế độ nền

Để đọc dữ liệu ở chế độ nền, hãy khai báo quyền sau đây trong tệp kê khai:

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

Ví dụ sau đây minh hoạ cách đọc dữ liệu về số bước của một người dùng trong một khoảng thời gian nhất định ở chế độ nền bằng cách sử dụng WorkManager:

class ScheduleWorker(appContext: Context, workerParams: WorkerParameters) :
    CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result {
        val healthConnectClient = HealthConnectClient.getOrCreate(applicationContext)
        // Perform background read logic here
        return Result.success()
    }
}
@OptIn(ExperimentalFeatureAvailabilityApi::class)
fun enqueueBackgroundReadWorker(context: Context, healthConnectClient: HealthConnectClient) {
    if (healthConnectClient
            .features
            .getFeatureStatus(
                HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
            ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE
    ) {

        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
}

Tham số ReadRecordsRequest có giá trị pageSize mặc định là 1000. Nếu số lượng bản ghi trong một readResponse vượt quá pageSize của yêu cầu, bạn cần lặp lại trên tất cả các trang của phản hồi để truy xuất tất cả bản ghi bằng cách sử dụng pageToken. Tuy nhiên, hãy cẩn thận để tránh các vấn đề về việc giới hạn tốc độ.

Ví dụ về cách đọc pageToken

Bạn nên dùng pageToken để đọc các bản ghi nhằm truy xuất tất cả dữ liệu có sẵn trong khoảng thời gian được yêu cầu.

Ví dụ sau đây minh hoạ cách đọc tất cả các bản ghi cho đến khi hết tất cả mã thông báo trang:

val type = HeartRateRecord::class
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofDays(7))

try {
    var pageToken: String? = null
    do {
        val readResponse =
            healthConnectClient.readRecords(
                ReadRecordsRequest(
                    recordType = type,
                    timeRangeFilter = TimeRangeFilter.between(
                        startTime,
                        endTime
                    ),
                    pageToken = pageToken
                )
            )
        val records = readResponse.records
        // Do something with records
        pageToken = readResponse.pageToken
    } while (pageToken != null)
} catch (quotaError: IllegalStateException) {
    // Backoff
}
Để biết thông tin về các phương pháp hay nhất khi đọc các tập dữ liệu lớn, hãy tham khảo bài viết Lập kế hoạch để tránh bị giới hạn tốc độ.

Đọc dữ liệu đã ghi trước đây

Nếu trước đây, một ứng dụng đã ghi bản ghi vào Health Connect, thì ứng dụng đó có thể đọc dữ liệu cũ. Cách này áp dụng cho các trường hợp mà trong đó, ứng dụng cần đồng bộ hoá lại với Health Connect sau khi người dùng cài đặt lại ứng dụng này.

Có một số hạn chế khi đọc:

  • Đối với Android 14 trở lên

    • Không có giới hạn về thời gian lưu trữ dữ liệu mà ứng dụng đọc dữ liệu của chính mình.
    • Giới hạn 30 ngày đối với việc một ứng dụng đọc dữ liệu khác.
  • Đối với Android 13 trở xuống

    • Giới hạn 30 ngày đối với việc ứng dụng đọc mọi dữ liệu.

Bạn có thể xoá các hạn chế này bằng cách yêu cầu Quyền đọc.

Để đọc dữ liệu trước đây, bạn cần biểu thị tên gói ở dạng đối tượng DataOrigin trong tham số dataOriginFilter của ReadRecordsRequest.

Ví dụ sau đây minh hoạ cách biểu thị tên gói khi đọc bản ghi về tần số tim:

try {
    val response =  healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = HeartRateRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
            dataOriginFilter = setOf(DataOrigin("com.my.package.name"))
        )
    )
    for (record in response.records) {
        // Process each record
    }
} catch (e: Exception) {
    // Run error handling here
}

Đọc dữ liệu cũ hơn 30 ngày

Theo mặc định, tất cả ứng dụng đều có thể đọc dữ liệu từ Health Connect trong vòng tối đa 30 ngày trước thời điểm cấp quyền lần đầu.

Nếu bạn cần mở rộng quyền đọc ngoài bất kỳ hạn chế mặc định nào, hãy yêu cầu PERMISSION_READ_HEALTH_DATA_HISTORY. Nếu không có quyền này, việc cố gắng đọc các bản ghi cũ hơn 30 ngày sẽ dẫn đến lỗi.

Nhật ký quyền của một ứng dụng đã bị xoá

Nếu người dùng xoá ứng dụng của bạn, tất cả các quyền, bao gồm cả quyền truy cập nhật ký, sẽ bị thu hồi. Nếu người dùng cài đặt lại ứng dụng của bạn và cấp lại quyền, thì các hạn chế mặc định tương tự sẽ được áp dụng và ứng dụng của bạn có thể đọc dữ liệu của Health Connect trong vòng tối đa 30 ngày trước ngày mới đó.

Ví dụ: giả sử người dùng xoá ứng dụng của bạn vào ngày 10 tháng 5 năm 2023 rồi cài đặt lại ứng dụng vào ngày 15 tháng 5 năm 2023 và cấp quyền đọc. Theo mặc định, ngày sớm nhất mà ứng dụng của bạn có thể đọc dữ liệu là ngày 15 tháng 4 năm 2023.

Xử lý các trường hợp ngoại lệ

Khi gặp sự cố, Health Connect sẽ gửi những trường hợp ngoại lệ tiêu chuẩn cho các hoạt động CRUD. Ứng dụng của bạn nên nắm bắt và xử lý từng trường hợp ngoại lệ theo cách phù hợp.

Mỗi phương thức trên HealthConnectClient liệt kê các trường hợp ngoại lệ có thể được gửi. Nhìn chung, ứng dụng của bạn phải xử lý các trường hợp ngoại lệ sau:

Bảng 1: Các trường hợp ngoại lệ và phương pháp hay nhất được đề xuất của Health Connect
Ngoại lệ Mô tả Phương pháp hay nhất được đề xuất
IllegalStateException Một trong các trường hợp sau đã xảy ra:

  • Dịch vụ Health Connect không hoạt động.
  • Yêu cầu không phải là một cấu trúc hợp lệ. Ví dụ: yêu cầu tổng hợp trong các bộ chứa định kỳ trong đó đối tượng Instant được dùng cho timeRangeFilter.

Xử lý các sự cố có thể xảy ra với dữ liệu đầu vào trước khi thực hiện yêu cầu. Bạn nên chỉ định giá trị cho các biến hoặc dùng các biến đó làm tham số trong một hàm tuỳ chỉnh thay vì sử dụng trực tiếp trong các yêu cầu để có thể áp dụng chiến lược xử lý lỗi.
IOException Đã xảy ra sự cố khi đọc và ghi dữ liệu từ ổ đĩa. Để tránh sự cố này, dưới đây là một số đề xuất:

  • Sao lưu mọi hoạt động đầu vào của người dùng.
  • Có thể xử lý mọi sự cố xảy ra trong khi ghi hàng loạt. Ví dụ: hãy đảm bảo quy trình này xử lý được sự cố và thực hiện các thao tác còn lại.
  • Áp dụng các chiến lược thử lại và thời gian đợi để xử lý các sự cố về yêu cầu.

RemoteException Đã xảy ra lỗi trong hoặc khi giao tiếp với dịch vụ cơ bản kết nối với SDK.

Ví dụ: ứng dụng của bạn đang cố gắng xoá một bản ghi có uid cụ thể. Tuy nhiên, trường hợp ngoại lệ sẽ được gửi sau khi ứng dụng phát hiện ra bản ghi không tồn tại trong quá trình kiểm tra dịch vụ cơ bản.
Để tránh sự cố này, dưới đây là một số đề xuất:

  • Thường xuyên đồng bộ hoá giữa kho dữ liệu của ứng dụng và Health Connect.
  • Áp dụng các chiến lược thử lại và thời gian đợi để xử lý các sự cố về yêu cầu.

SecurityException Đã xảy ra sự cố khi các yêu cầu này cần quyền không được cấp. Để tránh tình trạng này, hãy đảm bảo rằng bạn đã khai báo việc sử dụng các loại dữ liệu của Health Connect cho ứng dụng đã xuất bản. Ngoài ra, bạn phải khai báo các quyền của Health Connect trong tệp kê khaitrong hoạt động của bạn.