Synchronizuj dane

Ten przewodnik jest zgodny z wersją 1.1.0-alpha12 Health Connect.

Większość aplikacji zintegrowanych z Health Connect ma własny magazyn danych, który służy jako źródło wiarygodnych informacji. Health Connect oferuje sposoby synchronizacji aplikacji.

W zależności od architektury aplikacji proces synchronizacji może obejmować niektóre lub wszystkie z tych działań:

  • Przekazywanie nowych lub zaktualizowanych danych z pamięci danych aplikacji do Health Connect.
  • Pobierz zmiany danych z Health Connect do magazynu danych aplikacji.
  • Usuwanie danych z Health Connect, gdy zostaną one usunięte z pamięci danych aplikacji.

W każdym przypadku upewnij się, że proces synchronizacji zapewnia spójność danych w Health Connect i w pamięci danych aplikacji.

Przekazywanie danych do Health Connect

Pierwsza część procesu synchronizacji polega na przekazywaniu danych z pamięci danych aplikacji do pamięci danych Health Connect.

Przygotowywanie danych

Zwykle rekordy w pamięci danych aplikacji zawierają te informacje:

  • unikalny klucz, np. UUID;
  • wersję lub sygnaturę czasową;

Podczas synchronizowania danych z Health Connect identyfikuj i przesyłaj tylko te dane, które zostały wstawione, zaktualizowane lub usunięte od czasu ostatniej synchronizacji.

Zapisywanie danych w Health Connect

Aby przekazywać dane do Health Connect, wykonaj te czynności:

  1. Uzyskaj listę nowych, zaktualizowanych lub usuniętych wpisów z pamięci danych aplikacji.
  2. Dla każdego wpisu utwórz obiekt Record odpowiedni do danego typu danych. Możesz na przykład utworzyć obiekt WeightRecord dla danych związanych z wagą.
  3. Określ obiekt Metadata dla każdego elementu Record. Obejmuje to clientRecordId, czyli identyfikator z pamięci danych aplikacji, którego możesz używać do jednoznacznego identyfikowania rekordu. Możesz w tym celu użyć istniejącego unikalnego klucza. Jeśli Twoje dane są wersjonowane, podaj też clientRecordVersion, który jest zgodny z wersjonowaniem używanym w Twoich danych. Jeśli nie jest wersjonowany, możesz użyć wartości Long bieżącej sygnatury czasowej.

    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. Wstawiaj i aktualizuj dane w Health Connect za pomocą insertRecords. Wstawianie danych oznacza, że wszelkie istniejące dane w Health Connect zostaną zastąpione, o ile wartości clientRecordId znajdują się w magazynie danych Health Connect, a wartość clientRecordVersion jest wyższa od istniejącej. W przeciwnym razie dane wstawione lub zaktualizowane zostaną zapisane jako nowe dane.

    healthConnectClient.insertRecords(arrayListOf(record))
    

Aby dowiedzieć się więcej o praktycznych aspektach przesyłania danych, zapoznaj się ze sprawdzonymi metodami zapisywania danych.

Przechowywanie identyfikatorów Health Connect

Jeśli Twoja aplikacja odczytuje też dane z Health Connect, po wstawieniu lub zaktualizowaniu rekordów zapisz identyfikator id Health Connect. Jest to potrzebne do przetwarzania usunięć podczas pobierania zmian danych z Health Connect.id

Funkcja insertRecords zwraca element InsertRecordsResponse, który zawiera listę wartości id. Użyj odpowiedzi, aby uzyskać identyfikatory rekordów i je zapisać.

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

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

Pobieranie danych z Health Connect

Drugą częścią procesu synchronizacji jest pobieranie zmian danych z Health Connect do magazynu danych aplikacji. Zmiany danych mogą obejmować aktualizacje i usunięcia.

Pobieranie tokena zmian

Aby pobrać listę zmian z Health Connect, aplikacja musi śledzić tokeny Changes. Możesz ich używać podczas wysyłania żądań Changes, aby zwracać zarówno listę zmian danych, jak i nowy token Changes, który będzie używany przy następnym żądaniu.

Aby uzyskać token Changes, wywołaj funkcję getChangesToken i podaj wymagane typy danych.

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

Sprawdzanie zmian danych

Po uzyskaniu tokena Changes użyj go, aby pobrać wszystkie Changes. Zalecamy utworzenie pętli, która będzie przechodzić przez wszystkie zmiany i sprawdzać, czy są dostępne zmiany danych. Aby to zrobić:

  1. Wywołaj funkcję getChanges, używając tokena, aby uzyskać listę zmian.
  2. Sprawdź, czy typ zmiany to UpsertionChange czy DeletionChange, i wykonaj niezbędne operacje.
    • W przypadku UpsertionChange uwzględniaj tylko zmiany, które nie pochodzą z aplikacji do dzwonienia, aby uniknąć ponownego importowania danych.
  3. Przypisz kolejny token Changes jako nowy token.
  4. Powtarzaj kroki 1–3, aż nie będzie już żadnych zmian.
  5. Zapisz kolejny token i zarezerwuj go na potrzeby przyszłego importu.
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
}

Aby dowiedzieć się więcej o praktycznych aspektach pobierania danych, zapoznaj się ze sprawdzonymi metodami synchronizowania danych.

Przetwarzanie zmian w danych

odzwierciedlać zmiany w pamięci danych aplikacji; W przypadku UpsertionChange użyj idlastModifiedTimemetadata, aby wstawić lub zaktualizować rekord. W przypadku DeletionChange użyj id, aby usunąć rekord. Wymaga to zapisania rekordu id zgodnie z opisem w sekcji Przechowywanie identyfikatorów Health Connect.

Usuwanie danych z Health Connect

Gdy użytkownik usunie swoje dane z Twojej aplikacji, upewnij się, że zostaną one również usunięte z Health Connect. Aby to zrobić, użyj deleteRecords. Funkcja ta przyjmuje typ rekordu i listę wartości idclientRecordId, co ułatwia usuwanie wielu danych naraz. Dostępna jest też alternatywna funkcja deleteRecords, która przyjmuje argument timeRangeFilter.

Synchronizacja z urządzeń do noszenia z małym opóźnieniem

Aby synchronizować dane z urządzenia do śledzenia aktywności fizycznej z Health Connect z niskim opóźnieniem, użyj CompanionDeviceService. To podejście działa w przypadku urządzeń, które obsługują powiadomienia lub wskazania BLE GATT i są przeznaczone dla Androida 8.0 (poziom interfejsu API 26) lub nowszego. CompanionDeviceService umożliwia aplikacji odbieranie danych z urządzeń do noszenia i zapisywanie ich w Health Connect, nawet gdy aplikacja nie jest jeszcze uruchomiona. Więcej informacji o sprawdzonych metodach dotyczących BLE znajdziesz w tym artykule.

Powiąż urządzenie

Najpierw aplikacja musi przeprowadzić użytkownika przez jednorazowy proces powiązania urządzenia z aplikacją za pomocą interfejsu CompanionDeviceManager. Dzięki temu aplikacja uzyska uprawnienia niezbędne do korzystania z urządzenia. Więcej informacji znajdziesz w artykule Parowanie urządzenia towarzyszącego.

Zadeklaruj usługę w pliku manifestu

Następnie zadeklaruj uprawnienie CompanionDeviceService w pliku manifestu aplikacji. Dodaj do pliku AndroidManifest.xml te informacje:

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

Tworzenie usługi CompanionDeviceService

Na koniec utwórz klasę, która rozszerza klasę CompanionDeviceService. Ta usługa obsługuje połączenie z urządzeniem do noszenia i odbiera dane za pomocą wywołań zwrotnych BLE GATT. Gdy otrzymasz nowe dane, zostaną one natychmiast zapisane w 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()
   }
}

Sprawdzone metody synchronizowania danych

Na proces synchronizacji wpływają te czynniki:

Wygaśnięcie tokena

Nieużywany token Changes wygasa w ciągu 30 dni, więc musisz używać strategii synchronizacji, która zapobiega utracie informacji w takim przypadku. Twoja strategia może obejmować te podejścia:

  • Wyszukaj w pamięci danych aplikacji ostatnio użyty rekord, który zawiera też id z Health Connect.
  • Żądaj z Health Connect rekordów, które zaczynają się od określonego znacznika czasu, a następnie wstawiaj je lub aktualizuj w pamięci danych aplikacji.
  • Poproś o token zmian, aby zarezerwować go na następny raz, gdy będzie potrzebny.

Zalecane strategie zarządzania zmianami

Jeśli Twoja aplikacja otrzymuje nieprawidłowe lub wygasłe tokeny Changes, zalecamy stosowanie tych strategii zarządzania w zależności od ich zastosowania w logice aplikacji:

  • Odczytaj i usuń duplikaty wszystkich danych. Jest to najbardziej optymalna strategia.
    • przechowywać sygnaturę czasową ostatniego odczytu danych z Health Connect;
    • Po wygaśnięciu tokena ponownie odczytaj wszystkie dane z najnowszej sygnatury czasowej lub z ostatnich 30 dni. Następnie usuń duplikaty z wcześniej odczytanych danych za pomocą identyfikatorów.
    • Najlepiej zaimplementować identyfikatory klientów, ponieważ są one wymagane w przypadku aktualizacji danych.
  • Odczytuj tylko dane od ostatniej sygnatury czasowej odczytu. Powoduje to pewne rozbieżności w danych w okresie wygaśnięcia tokena zmian, ale okres ten jest krótszy i może trwać od kilku godzin do kilku dni.
    • przechowywać sygnaturę czasową ostatniego odczytu danych z Health Connect;
    • Po wygaśnięciu tokena odczytaj wszystkie dane od tej sygnatury czasowej.
  • Usuń, a potem odczytaj dane z ostatnich 30 dni. Jest to bardziej zgodne z tym, co dzieje się podczas pierwszej integracji.
    • Usunięcie wszystkich danych odczytanych przez aplikację z Health Connect w ciągu ostatnich 30 dni.
    • Po usunięciu odczytaj ponownie wszystkie te dane.
  • Odczytaj dane z ostatnich 30 dni bez usuwania duplikatów. Jest to najmniej optymalna strategia, która powoduje wyświetlanie użytkownikom zduplikowanych danych.
    • Usuń wszystkie dane odczytane przez aplikację z Health Connect w ciągu ostatnich 30 dni.
    • Zezwalaj na zduplikowane wpisy.

Tokeny zmian typu danych

Jeśli aplikacja korzysta z więcej niż jednego typu danych niezależnie od siebie, użyj oddzielnych tokenów Changes dla każdego typu danych. Listy wielu typów danych używaj w interfejsie API synchronizacji zmian tylko wtedy, gdy te typy danych są używane razem lub wcale.

Czytanie na pierwszym planie

Aplikacje mogą odczytywać dane z Health Connect tylko wtedy, gdy są na pierwszym planie. Podczas synchronizacji danych z Health Connect dostęp do tej usługi może zostać w każdej chwili przerwany. Na przykład aplikacja musi obsługiwać przerwy w synchronizacji w trakcie odczytywania dużej ilości danych z Health Connect i kontynuować działanie przy następnym otwarciu.

Odczytywanie w tle

Możesz poprosić, aby aplikacja działała w tle i odczytywała dane z Health Connect. Jeśli poprosisz o uprawnienie Background Read, użytkownik może przyznać aplikacji dostęp do odczytywania danych w tle.

Importowanie kodów czasowych

Ponieważ aplikacja nie może otrzymywać powiadomień o nowych danych, sprawdzaj, czy są dostępne, w 2 miejscach:

  • Za każdym razem, gdy aplikacja staje się aktywna na pierwszym planie. W takim przypadku użyj zdarzeń cyklu życia.
  • okresowo, gdy aplikacja działa na pierwszym planie; Powiadamiaj użytkowników o dostępności nowych danych, aby mogli zaktualizować ekran i odzwierciedlić zmiany.