Đồng bộ hoá dữ liệu

Hướng dẫn này tương thích với Health Connect phiên bản 1.1.0-alpha12.

Hầu hết ứng dụng tích hợp với Health Connect đều có kho dữ liệu riêng đóng vai trò là nguồn tập trung và đáng tin cậy. Health Connect cung cấp nhiều cách để bạn giữ cho ứng dụng của mình luôn đồng bộ.

Tuỳ thuộc vào cấu trúc ứng dụng, quy trình đồng bộ hoá có thể bao gồm một số hoặc tất cả các thao tác sau:

  • Cung cấp dữ liệu mới hoặc dữ liệu được cập nhật từ kho dữ liệu của ứng dụng lên Health Connect.
  • Lấy các thay đổi về dữ liệu từ Health Connect vào kho dữ liệu của ứng dụng.
  • Xoá dữ liệu khỏi Health Connect khi dữ liệu đó bị xoá trong kho dữ liệu của ứng dụng.

Trong mỗi trường hợp, hãy đảm bảo rằng quy trình đồng bộ hoá diễn ra thống nhất cho cả Health Connect và kho dữ liệu của ứng dụng.

Cấp dữ liệu cho Health Connect

Giai đoạn đầu tiên của quá trình đồng bộ hoá là chuyển dữ liệu từ kho dữ liệu của ứng dụng vào kho dữ liệu Health Connect.

Chuẩn bị dữ liệu của bạn

Thông thường, bản ghi trong kho dữ liệu của ứng dụng có các thông tin chi tiết sau:

  • Một khoá duy nhất, chẳng hạn như UUID.
  • Số phiên bản hoặc dấu thời gian.

Khi đồng bộ hoá dữ liệu với Health Connect, hãy xác định và chỉ cung cấp dữ liệu đã được chèn, cập nhật hoặc xoá kể từ lần đồng bộ hoá gần nhất.

Ghi dữ liệu vào Health Connect

Để cấp dữ liệu vào Health Connect, hãy làm theo các bước sau:

  1. Nhận danh sách các mục mới, đã cập nhật hoặc đã xoá khỏi kho dữ liệu của ứng dụng.
  2. Đối với mỗi mục, hãy tạo một đối tượng Record phù hợp với loại dữ liệu đó. Ví dụ: tạo một đối tượng WeightRecord cho dữ liệu liên quan đến cân nặng.
  3. Chỉ định một đối tượng Metadata cho mỗi Record. Trong đó có clientRecordId, là một mã nhận dạng trong kho dữ liệu của ứng dụng mà bạn có thể dùng để xác định duy nhất bản ghi. Bạn có thể sử dụng khoá duy nhất hiện có cho việc này. Nếu dữ liệu của bạn được lập phiên bản, hãy cung cấp clientRecordVersion phù hợp với quy trình lập phiên bản được dùng trong dữ liệu của bạn. Nếu dữ liệu chưa được tạo phiên bản, bạn có thể chuyển sang sử dụng giá trị Long của dấu thời gian hiện tại.

    val recordVersion = 0L
    // Specify as needed
    // The clientRecordId is an ID that you choose for your record. This
    // is often the same ID you use in your app's datastore.
    val clientRecordId = "<your-record-id>"
    
    val record = WeightRecord(
        metadata = Metadata.activelyRecorded(
            clientRecordId = clientRecordId,
            clientRecordVersion = recordVersion,
            device = Device(type = Device.TYPE_SCALE)
        ),
        weight = Mass.kilograms(62.0),
        time = Instant.now(),
        zoneOffset = ZoneOffset.UTC,
    )
    healthConnectClient.insertRecords(listOf()(record))
    
    
  4. Chèn và cập nhật dữ liệu cho Health Connect bằng cách sử dụng insertRecords. Việc chèn và cập nhật dữ liệu có nghĩa là mọi dữ liệu hiện có trong Health Connect sẽ bị ghi đè, chừng nào các giá trị clientRecordId trong kho dữ liệu Health Connect vẫn tồn tại và clientRecordVersion cao hơn giá trị hiện có. Nếu không, dữ liệu được chèn và cập nhật sẽ được ghi dưới dạng dữ liệu mới.

    healthConnectClient.insertRecords(arrayListOf(record))
    

Để tìm hiểu các vấn đề cần cân nhắc khi cấp dữ liệu trên thực tế, hãy xem các phương pháp hay nhất về Ghi dữ liệu.

Lưu trữ mã nhận dạng Health Connect

Nếu ứng dụng của bạn cũng đọc dữ liệu từ Health Connect, hãy lưu trữ id của Health Connect cho các bản ghi sau khi bạn chèn và cập nhật chúng. Bạn cần id này để xử lý các thao tác xoá khi lấy các thay đổi về dữ liệu từ Health Connect.

Hàm insertRecords sẽ trả về một InsertRecordsResponse có chứa danh sách giá trị id. Dùng phản hồi để nhận mã nhận dạng Record (Bản ghi) và lưu trữ các mã này.

val response = healthConnectClient.insertRecords(arrayListOf(record))

for (recordId in response.recordIdsList) {
    // Store recordId to your app's datastore
}

Lấy dữ liệu từ Health Connect

Phần thứ hai của quy trình đồng bộ hoá là lấy mọi thay đổi về dữ liệu từ Health Connect về kho dữ liệu của ứng dụng. Thay đổi về dữ liệu có thể gồm cả việc cập nhật và xoá.

Nhận mã thông báo Changes

Để nhận danh sách thay đổi cần lấy từ Health Connect, ứng dụng của bạn cần theo dõi mã thông báo Changes (Thay đổi). Bạn có thể sử dụng các mã thông báo này khi yêu cầu Changes trả về cả danh sách thay đổi về dữ liệu và mã thông báo Changes mới để dùng vào lần tiếp theo.

Để nhận mã thông báo Changes, hãy gọi getChangesToken và cung cấp các loại dữ liệu bắt buộc.

val changesToken = healthConnectClient.getChangesToken(
    ChangesTokenRequest(recordTypes = setOf(WeightRecord::class))
)

Kiểm tra các thay đổi về dữ liệu

Giờ bạn đã nhận được mã thông báo Changes, hãy sử dụng mã thông báo này để nhận tất cả Changes. Bạn nên tạo một vòng lặp để kiểm tra tất cả Changes xem có thay đổi nào về dữ liệu không. Sau đây là các bước thực hiện:

  1. Gọi getChanges bằng mã thông báo này để lấy danh sách Changes.
  2. Kiểm tra từng thay đổi để xem loại thay đổi là UpsertionChange hay DeletionChange, rồi thực hiện các thao tác cần thiết.
    • Đối với UpsertionChange, chỉ thực hiện các thay đổi không đến từ ứng dụng gọi để đảm bảo rằng bạn không nhập lại dữ liệu.
  3. Chỉ định mã thông báo Changes tiếp theo làm mã thông báo mới.
  4. Lặp lại Bước 1-3 cho đến khi không còn Changes nào.
  5. Lưu trữ mã thông báo tiếp theo và đặt trước mã đó để sau này nhập.
suspend fun processChanges(token: String): String {
    var nextChangesToken = token
    do {
        val response = healthConnectClient.getChanges(nextChangesToken)
        response.changes.forEach { change ->
            when (change) {
                is UpsertionChange ->
                    if (change.record.metadata.dataOrigin.packageName != context.packageName) {
                        processUpsertionChange(change)
                    }
                is DeletionChange -> processDeletionChange(change)
            }
        }
        nextChangesToken = response.nextChangesToken
    } while (response.hasMore)
    // Return and store the changes token for use next time.
    return nextChangesToken
}

Để tìm hiểu các vấn đề cần cân nhắc khi lấy dữ liệu trên thực tế, hãy xem các phương pháp hay nhất về Đồng bộ hoá dữ liệu.

Xử lý các thay đổi về dữ liệu

Phản ánh các thay đổi đối với kho dữ liệu của ứng dụng. Đối với UpsertionChange, hãy sử dụng idlastModifiedTime trong metadata để chèn và cập nhật bản ghi. Đối với DeletionChange, hãy dùng id được cung cấp để xoá bản ghi đó. Điều này đòi hỏi bạn phải lưu trữ bản ghi id như đã đề cập trong phần Lưu trữ mã nhận dạng Health Connect.

Xoá dữ liệu khỏi Health Connect

Khi người dùng xoá dữ liệu của họ khỏi ứng dụng của bạn, hãy nhớ xoá dữ liệu đó khỏi Health Connect. Hãy sử dụng deleteRecords để thực hiện việc này. Thao tác này sẽ lấy một loại bản ghi và danh sách các giá trị idclientRecordId để thuận tiện cho việc phân nhóm nhiều dữ liệu để xoá. Bạn cũng có thể dùng một deleteRecords thay thế nhận timeRangeFilter.

Đồng bộ hoá độ trễ thấp từ thiết bị đeo

Để đồng bộ hoá dữ liệu từ thiết bị thể dục đeo trên người với Health Connect có độ trễ thấp, hãy sử dụng CompanionDeviceService. Phương pháp này hoạt động đối với các thiết bị hỗ trợ Thông báo hoặc Chỉ báo BLE GATT và nhắm đến Android 8.0 (API cấp 26) trở lên. CompanionDeviceService cho phép ứng dụng của bạn nhận dữ liệu từ thiết bị đeo và ghi dữ liệu đó vào Health Connect, ngay cả khi ứng dụng chưa chạy. Để biết thêm thông tin về các phương pháp hay nhất cho BLE, hãy xem bài viết Tổng quan về Bluetooth năng lượng thấp.

Liên kết thiết bị

Trước tiên, ứng dụng của bạn phải hướng dẫn người dùng thực hiện quy trình một lần để liên kết thiết bị đeo với ứng dụng bằng CompanionDeviceManager. Thao tác này cấp cho ứng dụng của bạn các quyền cần thiết để tương tác với thiết bị. Để biết thêm thông tin, hãy xem phần Ghép nối thiết bị đồng hành.

Khai báo dịch vụ trong tệp kê khai

Tiếp theo, hãy khai báo CompanionDeviceService trong tệp kê khai của ứng dụng. Thêm nội dung sau vào AndroidManifest.xml:

<manifest ...>
   <application ...>
       <service
           android:name=".MyWearableService"
           android:exported="true"
           android:permission="android.permission.BIND_COMPANION_DEVICE_SERVICE">
           <intent-filter>
               <action android:name="android.companion.CompanionDeviceService" />
           </intent-filter>
       </service>
   </application>
</manifest>

Tạo CompanionDeviceService

Cuối cùng, hãy tạo một lớp mở rộng CompanionDeviceService. Dịch vụ này xử lý kết nối với thiết bị đeo và nhận dữ liệu thông qua các lệnh gọi lại GATT BLE. Khi nhận được dữ liệu mới, dữ liệu đó sẽ được ghi ngay vào Health Connect.

import android.companion.CompanionDeviceService
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import androidx.health.connect.client.permission.HealthPermission
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.StepsRecord
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch

class MyWearableService : CompanionDeviceService() {

   // A coroutine scope for handling suspend functions like writing to Health Connect
   private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
   private var healthConnectClient: HealthConnectClient? = null
   private var bluetoothGatt: BluetoothGatt? = null

   // This is called by the system when your wearable connects
   override fun onDeviceAppeared(address: String) {
       super.onDeviceAppeared(address)
       healthConnectClient = HealthConnectClient.getOrCreate(this)

       serviceScope.launch {
           // Check which permissions have been granted before subscribing to data from the wearable.
           // A service cannot request permissions, so your app must have already requested
           // and been granted them from an Activity.
           val granted = healthConnectClient?.permissionController?.getGrantedPermissions()

           // ... set up your GATT connection here ...

           // Once connected, subscribe to notifications for the data types you have
           // permission to write.
           if (granted?.contains(HealthPermission.getWritePermission(HeartRateRecord::class)) == true) {
               // subscribeToHeartRate(bluetoothGatt)
           }
       }
   }

   // The core of your low-latency pipeline is the BLE callback
   private val gattCallback = object : BluetoothGattCallback() {
       override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
           super.onCharacteristicChanged(gatt, characteristic, value)

           // 1. Instantly receive the data
           val rawData = value

           // 2. Parse the data from the wearable
           val healthData = parseWearableData(rawData) // Your custom parsing logic

           // 3. Immediately process it. For simplicity, this example writes
           //    directly to Health Connect. A real-world app might write to its
           //    own datastore first and then sync with Health Connect.
           serviceScope.launch {
               writeToHealthConnect(healthData)
           }
       }
   }

   private suspend fun writeToHealthConnect(healthData: HealthData) {
       val records = prepareHealthConnectRecords(healthData) // Convert to Health Connect records
       try {
           healthConnectClient?.insertRecords(records)
       } catch (e: Exception) {
           // Handle exceptions
       }
   }

   // This is called by the system when your wearable disconnects
   override fun onDeviceDisappeared(address: String) {
       super.onDeviceDisappeared(address)
       // Clean up your GATT connection and other resources
       bluetoothGatt?.close()
   }
}

Các phương pháp hay nhất để đồng bộ hoá dữ liệu

Các yếu tố sau ảnh hưởng đến quá trình đồng bộ hoá.

Thời hạn của mã thông báo

Vì một mã thông báo Thay đổi không sử dụng sẽ hết hạn trong vòng 30 ngày, nên bạn phải sử dụng chiến lược đồng bộ hoá để tránh mất thông tin trong trường hợp như vậy. Chiến lược của bạn có thể bao gồm các phương pháp sau:

  • Vào kho dữ liệu của ứng dụng để tìm bản ghi được sử dụng gần đây nhất cũng có id giống của Health Connect.
  • Yêu cầu các bản ghi của Health Connect bắt đầu bằng một dấu thời gian cụ thể, sau đó chèn hoặc cập nhật các bản ghi đó trong kho dữ liệu của ứng dụng.
  • Yêu cầu một mã thông báo Thay đổi để dự trữ cho lần tiếp theo cần đến.

Chiến lược quản lý Thay đổi được đề xuất

Trong trường hợp ứng dụng của bạn nhận được mã thông báo Thay đổi không hợp lệ hoặc đã hết hạn, bạn nên sử dụng các chiến lược quản lý sau đây, tuỳ thuộc vào ứng dụng của nó trong logic:

  • Đọc và loại bỏ trùng lặp tất cả dữ liệu. Đây là chiến lược lý tưởng nhất.
    • Lưu trữ dấu thời gian của lần gần nhất ứng dụng đọc dữ liệu từ Health Connect.
    • Khi mã thông báo hết hạn, hãy đọc lại tất cả dữ liệu từ dấu thời gian gần đây nhất hoặc trong 30 ngày qua. Sau đó, loại bỏ trùng lặp đối với dữ liệu đã đọc trước đây bằng cách sử dụng giá trị nhận dạng.
    • Tốt nhất là bạn nên triển khai Mã ứng dụng khách vì đây là những mã bắt buộc để cập nhật dữ liệu.
  • Chỉ đọc dữ liệu từ dấu thời gian của lần đọc gần nhất. Chiến lược này dẫn đến một số khác biệt về dữ liệu trong khoảng thời gian mã thông báo Thay đổi hết hạn. Tuy nhiên, khoảng thời gian ngắn hơn có thể mất từ vài giờ đến vài ngày.
    • Lưu trữ dấu thời gian của lần gần nhất ứng dụng đọc dữ liệu từ Health Connect.
    • Khi mã thông báo hết hạn, hãy đọc tất cả dữ liệu từ dấu thời gian này trở đi.
  • Xoá rồi đọc dữ liệu trong 30 ngày qua. Chiến lược này điều chỉnh cho phù hợp hơn với những gì diễn ra ở lần tích hợp đầu tiên.
    • Xoá tất cả dữ liệu mà ứng dụng đã đọc từ Health Connect trong 30 ngày qua.
    • Sau khi xoá, hãy đọc lại tất cả dữ liệu này.
  • Đọc dữ liệu trong 30 ngày qua mà không loại bỏ trùng lặp. Đây là chiến lược ít lý tưởng nhất vì sẽ khiến người dùng nhìn thấy dữ liệu trùng lặp.
    • Xoá tất cả dữ liệu mà ứng dụng đã đọc từ Health Connect trong 30 ngày qua.
    • Cho phép các mục nhập trùng lặp.

Mã thông báo thay đổi về loại dữ liệu

Nếu ứng dụng của bạn sử dụng độc lập nhiều loại dữ liệu, hãy dùng các Mã thông báo thay đổi riêng biệt cho từng loại dữ liệu. Chỉ sử dụng danh sách nhiều loại dữ liệu cùng với Changes Sync API (API Đồng bộ hoá thay đổi) nếu các loại dữ liệu này được hoặc không được sử dụng cùng nhau.

Đọc trên nền trước

Các ứng dụng chỉ có thể đọc dữ liệu của Health Connect khi đang ở nền trước. Khi ứng dụng đồng bộ hoá dữ liệu của Health Connect, quyền truy cập vào Health Connect có thể bị gián đoạn bất cứ lúc nào. Ví dụ: ứng dụng của bạn sẽ phải xử lý tình trạng gián đoạn ở giữa chừng quá trình đồng bộ hoá khi đọc một lượng lớn dữ liệu từ Health Connect, rồi tiếp tục vào lần tiếp theo ứng dụng mở ra.

Đọc ở chế độ nền

Bạn có thể yêu cầu ứng dụng chạy ở chế độ nền và đọc dữ liệu từ Health Connect. Nếu bạn yêu cầu quyền Background Read, người dùng có thể cấp cho ứng dụng của bạn quyền truy cập để đọc dữ liệu ở chế độ nền.

Xác định thời gian nhập

Vì ứng dụng của bạn không thể nhận thông báo về dữ liệu mới nên hãy kiểm tra dữ liệu mới ở 2 thời điểm:

  • Mỗi khi ứng dụng của bạn hoạt động ở nền trước. Trong trường hợp này, hãy sử dụng các sự kiện trong vòng đời.
  • Định kỳ khi ứng dụng của bạn duy trì hoạt động ở nền trước. Thông báo cho người dùng khi có dữ liệu mới, cho phép họ cập nhật màn hình để phản ánh các thay đổi.