Daten synchronisieren

Diese Anleitung ist mit Health Connect-Version 1.1.0-alpha12 oder höher kompatibel.

Die meisten Apps, die in Health Connect integriert sind, haben einen eigenen Datenspeicher, der als Source of Truth dient. Health Connect bietet Möglichkeiten, Ihre App zu synchronisieren.

Je nach Architektur Ihrer Anwendung kann der Synchronisierungsvorgang einige oder alle der folgenden Aktionen umfassen:

  • Neue oder aktualisierte Daten aus dem Datenspeicher deiner App an Health Connect senden.
  • Datenänderungen aus Health Connect in den Datenspeicher Ihrer App übertragen.
  • Daten aus Health Connect löschen, wenn sie im Datenspeicher Ihrer App gelöscht werden.

Achte in jedem Fall darauf, dass der Synchronisierungsvorgang sowohl Health Connect als auch den Datenspeicher deiner App auf dem gleichen Stand hält.

Daten an Health Connect senden

Im ersten Teil des Synchronisierungsprozesses werden Daten aus dem Datenspeicher Ihrer App in den Health Connect-Datenspeicher übertragen.

Daten vorbereiten

In der Regel enthalten die Einträge im Datenspeicher Ihrer Anwendung die folgenden Details:

  • Einen eindeutigen Schlüssel, z. B. einen UUID.
  • Eine Version oder ein Zeitstempel.

Wenn du Daten mit Health Connect synchronisierst, solltest du nur die Daten identifizieren und einspeisen, die seit der letzten Synchronisierung eingefügt, aktualisiert oder gelöscht wurden.

Daten in Health Connect schreiben

So speisen Sie Daten in Health Connect ein:

  1. Rufen Sie eine Liste der neuen, aktualisierten oder gelöschten Einträge aus dem Datenspeicher Ihrer App ab.
  2. Erstellen Sie für jeden Eintrag ein Record-Objekt, das für diesen Datentyp geeignet ist. Erstelle beispielsweise ein WeightRecord-Objekt für Daten zum Gewicht.
  3. Geben Sie für jede Record ein Metadata-Objekt an. Dazu gehört auch clientRecordId, eine ID aus dem Datenspeicher Ihrer App, mit der Sie den Datensatz eindeutig identifizieren können. Sie können dafür Ihren vorhandenen eindeutigen Schlüssel verwenden. Wenn Ihre Daten versioniert sind, geben Sie außerdem einen clientRecordVersion an, der mit der Versionierung Ihrer Daten übereinstimmt. Wenn es nicht versioniert ist, können Sie alternativ den Long-Wert des aktuellen Zeitstempels verwenden.

    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. Daten mit insertRecords in Health Connect einfügen oder aktualisieren. Beim Upserting von Daten werden alle vorhandenen Daten in Health Connect überschrieben, sofern die clientRecordId-Werte im Health Connect-Datenspeicher vorhanden sind und der clientRecordVersion höher als der vorhandene Wert ist. Andernfalls werden die per Upsert eingefügten Daten als neue Daten geschrieben.

    healthConnectClient.insertRecords(arrayListOf(record))

Informationen zu den praktischen Überlegungen für die Dateneinspeisung finden Sie in den Best Practices für Daten schreiben.

Health Connect-IDs speichern

Wenn deine App auch Daten aus Health Connect liest, speichere die Health Connect-id für Datensätze, nachdem du sie aktualisiert hast. Sie benötigen diese id, um Löschvorgänge zu verarbeiten, wenn Sie Datenänderungen aus Health Connect abrufen.

Die Funktion insertRecords gibt einen InsertRecordsResponse zurück, der die Liste der id-Werte enthält. Verwenden Sie die Antwort, um die Eintrags-IDs abzurufen und zu speichern.

val response = healthConnectClient.insertRecords(listOf(record))
for (recordId in response.recordIdsList) {
    // Store recordId to your app's datastore
}

Daten aus Health Connect abrufen

Im zweiten Teil des Synchronisierungsprozesses werden alle Datenänderungen von Health Connect in den Datenspeicher Ihrer App übertragen. Die Datenänderungen können Aktualisierungen und Löschungen umfassen.

Änderungstoken abrufen

Damit Ihre App eine Liste der Änderungen erhält, die aus Health Connect abgerufen werden sollen, muss sie Änderungstokens im Blick behalten. Sie können sie verwenden, wenn Sie Änderungen anfordern, um sowohl eine Liste der Datenänderungen als auch ein neues Änderungstoken zurückzugeben, das beim nächsten Mal verwendet werden soll.

Wenn Sie ein Changes-Token erhalten möchten, rufen Sie getChangesToken auf und geben Sie die erforderlichen Datentypen an.

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

Nach Datenänderungen suchen

Nachdem Sie ein Changes-Token erhalten haben, können Sie damit alle Changes abrufen. Wir empfehlen, eine Schleife zu erstellen, um alle Änderungen zu durchlaufen und zu prüfen, ob Datenänderungen verfügbar sind. Gehen Sie dazu so vor:

  1. Rufen Sie getChanges mit dem Token auf, um eine Liste der Änderungen abzurufen.
  2. Prüfen Sie bei jeder Änderung, ob es sich um eine UpsertionChange oder eine DeletionChange handelt, und führen Sie die erforderlichen Vorgänge aus.
    • Übernehmen Sie für UpsertionChange nur Änderungen, die nicht von der aufrufenden App stammen, um sicherzustellen, dass Sie keine Daten noch einmal importieren.
  3. Weisen Sie das nächste Changes-Token als neues Token zu.
  4. Wiederholen Sie die Schritte 1 bis 3, bis keine Änderungen mehr vorhanden sind.
  5. Speichern Sie das nächste Token und reservieren Sie es für einen zukünftigen Import.

suspend fun processChanges(context: Context, 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
}

Informationen zu den praktischen Überlegungen zum Abrufen von Daten finden Sie in den Best Practices für die Datensynchronisierung.

Datenänderungen verarbeiten

Übernehmen Sie die Änderungen in den Datenspeicher Ihrer Anwendung. Verwenden Sie für UpsertionChange die id und die lastModifiedTime aus der metadata, um den Eintrag per Upsert zu aktualisieren. Verwenden Sie für DeletionChange die id, die zum Löschen des Eintrags bereitgestellt wurde. Dazu musst du den Eintrag id wie unter Health Connect-IDs speichern beschrieben gespeichert haben.

Daten aus Health Connect löschen

Wenn ein Nutzer seine eigenen Daten aus deiner App löscht, achte darauf, dass die Daten auch aus Health Connect entfernt werden. Verwenden Sie dazu deleteRecords. Dazu werden ein Datensatztyp und eine Liste mit id- und clientRecordId-Werten verwendet. So können mehrere Daten im Batch gelöscht werden. Es ist auch eine alternative deleteRecords verfügbar, die einen timeRangeFilter akzeptiert.

Synchronisierung von Wearables mit niedriger Latenz

Wenn du Daten von einem tragbaren Fitnessgerät mit geringer Latenz mit Health Connect synchronisieren möchtest, verwende CompanionDeviceService. Dieser Ansatz funktioniert für Geräte, die BLE-GATT-Benachrichtigungen oder -Anzeigen unterstützen und auf Android 8.0 (API-Level 26) oder höher ausgerichtet sind. CompanionDeviceService ermöglicht es deiner App, Daten von Wearables zu empfangen und in Health Connect zu schreiben, auch wenn die App nicht ausgeführt wird. Weitere Informationen zu Best Practices für BLE findest du in der Übersicht zu Bluetooth Low Energy.

Gerät verknüpfen

Zuerst muss deine App den Nutzer durch einen einmaligen Prozess führen, um das Wearable mithilfe von CompanionDeviceManager mit deiner App zu verknüpfen. Dadurch erhält Ihre App die erforderlichen Berechtigungen für die Interaktion mit dem Gerät. Weitere Informationen findest du unter Kopplung von Companion-Geräten.

Dienst im Manifest deklarieren

Deklarieren Sie als Nächstes CompanionDeviceService in der Manifestdatei Ihrer App. Fügen Sie Folgendes zu AndroidManifest.xml hinzu:

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

CompanionDeviceService erstellen

Erstellen Sie abschließend eine Klasse, die CompanionDeviceService erweitert. Dieser Dienst verarbeitet die Verbindung zum Wearable und empfängt Daten über BLE-GATT-Callbacks. Wenn neue Daten empfangen werden, werden sie sofort in Health Connect geschrieben.

private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private var healthConnectClient: HealthConnectClient? = null
private var bluetoothGatt: BluetoothGatt? = null

override fun onDeviceAppeared(address: String) {
    super.onDeviceAppeared(address)
    healthConnectClient = HealthConnectClient.getOrCreate(this)

    serviceScope.launch {
        val granted = healthConnectClient?.permissionController?.getGrantedPermissions()

        // 1. Check permissions ONCE when the device connects
        if (granted?.contains(HealthPermission.getWritePermission(HeartRateRecord::class)) ?: false) {
            // This is where you'd actually start the Bluetooth connection
            // bluetoothGatt = gattCallback.connect(...)
        }

        // 2. Do your initial database read
        readExerciseSessionAndRoute()
    }
}

private val gattCallback = object : BluetoothGattCallback() {
    override fun onCharacteristicChanged(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        value: ByteArray
    ) {
        super.onCharacteristicChanged(gatt, characteristic, value)

        // 3. ONLY process the incoming data here
        val rawData = value

        serviceScope.launch {
            // parseWearableData(rawData)
            // insertExerciseRoute() or writeToHealthConnect()
        }
    }
}

Best Practices für die Synchronisierung von Daten

Die folgenden Faktoren beeinflussen den Synchronisierungsvorgang.

Tokenablauf

Da ein nicht verwendetes Changes-Token innerhalb von 30 Tagen abläuft, müssen Sie eine Synchronisierungsstrategie verwenden, die in einem solchen Fall den Verlust von Informationen verhindert. Ihre Strategie könnte die folgenden Ansätze umfassen:

  • Suchen Sie im Datenspeicher Ihrer App nach dem zuletzt verwendeten Datensatz, der auch eine id von Health Connect hat.
  • Fordern Sie Datensätze von Health Connect an, die mit einem bestimmten Zeitstempel beginnen, und fügen Sie sie dann in den Datenspeicher Ihrer App ein oder aktualisieren Sie sie.
  • Fordern Sie ein Änderungstoken an, um es für die nächste Verwendung zu reservieren.

Empfohlene Strategien für das Änderungsmanagement

Wenn deine App ungültige oder abgelaufene Changes-Tokens erhält, empfehlen wir je nach Anwendung in deiner Logik die folgenden Verwaltungsstrategien:

  • Alle Daten lesen und deduplizieren. Dies ist die ideale Strategie.
    • Speichere den Zeitstempel des letzten Lesevorgangs von Daten aus Health Connect.
    • Nach Ablauf des Tokens werden alle Daten ab dem letzten Zeitstempel oder für die letzten 30 Tage noch einmal gelesen. Anschließend werden die Daten anhand von Kennungen mit den zuvor gelesenen Daten abgeglichen.
    • Idealerweise sollten Sie Client-IDs implementieren, da sie für Datenaktualisierungen erforderlich sind.
  • Nur Daten seit dem letzten Lesevorgang lesen. Dies führt zu einigen Datenabweichungen um den Zeitpunkt des Ablaufs des Änderungstokens, aber der Zeitraum ist kürzer und kann einige Stunden bis zu ein paar Tage dauern.
    • Speichere den Zeitstempel des letzten Lesevorgangs von Daten aus Health Connect.
    • Nach Ablauf des Tokens werden alle Daten ab diesem Zeitstempel gelesen.
  • Daten der letzten 30 Tage löschen und dann lesen. Dies entspricht eher dem, was bei der ersten Integration passiert.
    • Alle Daten löschen, die die App in den letzten 30 Tagen aus Health Connect gelesen hat.
    • Lies alle diese Daten nach dem Löschen noch einmal.
  • Daten der letzten 30 Tage ohne Deduplizierung lesen. Dies ist die am wenigsten ideale Strategie und führt dazu, dass Nutzern doppelte Daten angezeigt werden.
    • Alle Daten löschen, die die App in den letzten 30 Tagen aus Health Connect gelesen hat.
    • Doppelte Einträge zulassen.

Tokens für Änderungen am Datentyp

Wenn deine App mehr als einen Datentyp unabhängig voneinander nutzt, verwende für jeden Datentyp separate Änderungstokens. Verwenden Sie eine Liste mit mehreren Datentypen mit der Changes Sync API nur, wenn diese Datentypen entweder zusammen oder gar nicht verwendet werden.

Lesevorgänge im Vordergrund

Apps können nur dann Daten aus Health Connect lesen, wenn sie im Vordergrund ausgeführt werden. Beim Synchronisieren von Daten aus Health Connect kann der Zugriff auf Health Connect jederzeit unterbrochen werden. Ihre App muss beispielsweise Unterbrechungen während einer Synchronisierung verarbeiten können, wenn eine große Datenmenge aus Health Connect gelesen wird, und die Synchronisierung beim nächsten Öffnen der App fortsetzen.

Lesevorgänge im Hintergrund

Sie können anfordern, dass Ihre Anwendung im Hintergrund ausgeführt wird und Daten aus Health Connect liest. Wenn du die Berechtigung Background Read anforderst, kann der Nutzer deiner App Zugriff zum Lesen von Daten im Hintergrund gewähren.

Importzeiten

Da Ihre App nicht über neue Daten benachrichtigt werden kann, sollten Sie an zwei Stellen nach neuen Daten suchen:

  • Jedes Mal, wenn Ihre App im Vordergrund aktiv wird. Verwenden Sie in diesem Fall Lifecycle-Events.
  • Regelmäßig, solange Ihre App im Vordergrund ausgeführt wird. Benachrichtigen Sie Nutzer, wenn neue Daten verfügbar sind, damit sie ihren Bildschirm aktualisieren können, um die Änderungen zu sehen.