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

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.readRecords(
            ReadRecordsRequest(
                StepsRecord::class,
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        for (record in response.records) {
            // Process each record
        }
    } catch (e: Exception) {
        // Run error handling here
    }
}

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

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

Đọc dữ liệu về số bước trên thiết bị di động

Với Android 14 (API cấp 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 đi từ thiết bị chạy Android và người dùng sẽ thấy dữ liệu về số bước đi được tự động thêm vào các mục Số bước đi 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, bạn cần xác minh rằng thiết bị đang chạy Android 14 (API cấp 34) và có ít nhất SDK phiên bản tiện ích 20. Bạn có thể sử dụng mã sau:

val isStepTrackingAvailable =
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
        SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20

Số bước di chuyển trên thiết bị di động do Health Connect ghi lại có DataOrigin được đặt thành tên gói android. Nếu ứng dụng của bạn chỉ đọc số bước tổng hợp bằng cách sử dụ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ố.

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 phân tích theo ứng dụng hoặc thiết bị nguồn, thì bạn có thể truy vấn các bản ghi có DataOriginandroid. Nếu ứng dụng của bạn cho thấy thông tin phân bổ cho dữ liệu bước, bạn nên phân bổ dữ liệu từ gói Android cho thiết bị hiện tại. Bạn có thể thực hiện việc này bằng cách sử dụng một nhãn như "Điện thoại của bạn", truy xuất tên thiết bị bằng Settings.Global.getString(resolver, Settings.Global.DEVICE_NAME) hoặc kiểm tra trường Device trong siêu dữ liệu của bản ghi.

Ví dụ sau đây cho thấy cách đọc dữ liệu tổng hợp về số bước đi trên thiết bị di động bằng cách lọc theo nguồn dữ liệu android:

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = setOf(DataOrigin("android"))
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

Đếm bước trên thiết bị

Tìm hiểu kỹ hơn về tính năng đếm 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 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 của Health Connect không thường xuyên hơn 1 lần/phút.
  • Phân bổ: Như đã đề cập trước đó, tất cả các bước do tính năng trên thiết bị này ghi lại đều được phân bổ cho tên gói android trong DataOrigin.
  • 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(private val appContext: Context, workerParams: WorkerParameters):
    CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result {
        // Read data and process it.
        ...

        // Return success indicating successful data retrieval
        return Result.success()
    }
}

if (healthConnectClient
    .features
    .getFeatureStatus(
    HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
    ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {

    // Check if necessary permission is granted
    val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()

    if (PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND !in grantedPermissions) {
        // Perform read in foreground
        ...
    } else {
        // Schedule the periodic work request in background
        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
} else {
  // Background reading is not available, perform read in foreground
  ...
}

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ề 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 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ó thể đọ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ế bằng cách yêu cầu Quyền đọc.

Để đọc dữ liệu trong quá khứ, 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ề nhịp 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.