Daten synchronisieren

Dieser Leitfaden ist mit Health Connect-Version 1.1.0-alpha12 kompatibel.

Die meisten Apps, die in Health Connect eingebunden sind, haben einen eigenen Datenspeicher, der als Quelle der Wahrheit dient. Health Connect bietet Möglichkeiten, Ihre App zu synchronisieren.

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

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

Achten Sie in jedem Fall darauf, dass der Synchronisierungsprozess sowohl Health Connect als auch den Datenspeicher Ihrer App auf dem neuesten 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

Einträge im Datastore Ihrer App enthalten in der Regel die folgenden Details:

  • Ein eindeutiger Schlüssel, z. B. ein UUID.
  • Eine Version oder ein Zeitstempel.

Wenn Sie Daten mit Health Connect synchronisieren, identifizieren und übertragen Sie nur die Daten, die seit der letzten Synchronisierung eingefügt, aktualisiert oder gelöscht wurden.

Daten in Health Connect schreiben

So übertragen Sie Daten an Health Connect:

  1. Eine Liste mit neuen, aktualisierten oder gelöschten Einträgen aus dem Datenspeicher Ihrer App abrufen.
  2. Erstellen Sie für jeden Eintrag ein Record-Objekt, das für diesen Datentyp geeignet ist. Erstellen Sie beispielsweise ein WeightRecord-Objekt für Daten zum Gewicht.
  3. Geben Sie für jeden 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 auch eine clientRecordVersion an, die mit der in Ihren Daten verwendeten Versionierung übereinstimmt. Wenn sie nicht versioniert ist, können Sie stattdessen 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. Upsert-Daten in Health Connect mit insertRecords. 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-Wert höher als der vorhandene Wert ist. Andernfalls werden die eingefügten Daten als neue Daten geschrieben.

    healthConnectClient.insertRecords(arrayListOf(record))
    

Informationen zu den praktischen Aspekten der Datenbereitstellung finden Sie unter Daten schreiben.

Health Connect-IDs speichern

Wenn Ihre App auch Daten aus Health Connect liest, speichern Sie die Health Connect-id für Datensätze, nachdem Sie sie eingefügt oder aktualisiert haben. Sie benötigen diese id, um Löschvorgänge zu verarbeiten, wenn Sie Datenänderungen aus Health Connect abrufen.

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

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

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

Daten aus Health Connect abrufen

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

Changes-Token abrufen

Damit deine App eine Liste der Änderungen abrufen kann, die aus Health Connect übernommen werden sollen, muss sie Changes-Tokens im Blick behalten. Sie können sie verwenden, wenn Sie Changes anfordern, um sowohl eine Liste der Datenänderungen als auch ein neues Changes-Token zurückzugeben, das beim nächsten Mal verwendet werden soll.

Rufen Sie getChangesToken auf und geben Sie die erforderlichen Datentypen an, um ein Changes-Token zu erhalten.

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 Changes (Änderungen) zu durchlaufen und zu prüfen, ob Datenänderungen verfügbar sind. So gehts:

  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.
    • Für UpsertionChange sollten Sie nur Änderungen berücksichtigen, die nicht von der Anruf-App stammen, damit 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(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 Aspekten des Abrufens von Daten finden Sie unter Daten synchronisieren.

Datenänderungen verarbeiten

Die Änderungen im Datenspeicher Ihrer App widerspiegeln. Verwenden Sie für UpsertionChange die id und die lastModifiedTime aus dem metadata, um den Datensatz upsert. Verwenden Sie für DeletionChange die bereitgestellte id, um den Datensatz zu löschen. Dafür müssen Sie den Datensatz id wie unter Health Connect-IDs speichern beschrieben gespeichert haben.

Daten aus Health Connect löschen

Wenn ein Nutzer seine eigenen Daten aus Ihrer App löscht, müssen Sie dafür sorgen, dass die Daten auch aus Health Connect entfernt werden. Verwenden Sie dazu deleteRecords. Dazu sind ein Datensatztyp und eine Liste von id- und clientRecordId-Werten erforderlich. So lassen sich mehrere Daten bequem in einem Batch löschen. Alternativ ist auch eine deleteRecords verfügbar, die eine timeRangeFilter akzeptiert.

Synchronisierung mit niedriger Latenz von Wearables

Wenn Sie Daten von einem Wearable mit Fitness-Tracker mit geringer Latenz mit Health Connect synchronisieren möchten, verwenden Sie CompanionDeviceService. Dieser Ansatz funktioniert für Geräte, die BLE GATT-Benachrichtigungen oder -Hinweise unterstützen und auf Android 8.0 (API-Level 26) oder höher ausgerichtet sind. Mit CompanionDeviceService kann Ihre App Daten von Wearables empfangen und in Health Connect schreiben, auch wenn die App nicht bereits ausgeführt wird. Weitere Informationen zu BLE-Best Practices finden Sie unter Bluetooth Low Energy – Übersicht.

Gerät verknüpfen

Zuerst muss Ihre App den Nutzer durch einen einmaligen Prozess führen, um das Wearable mit Ihrer App zu verknüpfen. Dazu muss CompanionDeviceManager verwendet werden. Dadurch erhält Ihre App die erforderlichen Berechtigungen für die Interaktion mit dem Gerät. Weitere Informationen finden Sie unter Kopplung mit 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 schließlich eine Klasse, die CompanionDeviceService erweitert. Dieser Dienst stellt die Verbindung zum Wearable her und empfängt Daten über BLE GATT-Callbacks. Wenn neue Daten empfangen werden, werden sie sofort in Health Connect gespeichert.

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()
   }
}

Best Practices für die Synchronisierung von Daten

Die folgenden Faktoren wirken sich auf den Synchronisierungsvorgang aus.

Ablauf des Tokens

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

  • Suchen Sie im Datenspeicher Ihrer App nach dem zuletzt verwendeten Datensatz, der auch eine id aus Health Connect enthält.
  • Sie können Datensätze von Health Connect anfordern, die mit einem bestimmten Zeitstempel beginnen, und sie dann in den Datenspeicher Ihrer App einfügen oder aktualisieren.
  • Fordern Sie ein Changes-Token an, um es für die nächste Verwendung zu reservieren.

Empfohlene Strategien für die Verwaltung von Änderungen

Wenn Ihre App ungültige oder abgelaufene Changes-Tokens erhält, empfehlen wir die folgenden Verwaltungsstrategien, je nachdem, wie sie in Ihrer Logik angewendet werden:

  • Alle Daten lesen und deduplizieren: Das ist die beste Strategie.
    • Speichern des Zeitstempels des letzten Lesens von Daten aus Health Connect.
    • Wenn das Token abläuft, lesen Sie alle Daten ab dem letzten Zeitstempel oder für die letzten 30 Tage noch einmal. Anschließend werden die Daten anhand von Kennungen mit den zuvor gelesenen Daten dedupliziert.
    • Idealerweise implementieren Sie Client-IDs, da sie für Datenaktualisierungen erforderlich sind.
  • Nur Daten seit dem letzten Zeitstempel für den Lesevorgang lesen: Dies führt zu einigen Datenabweichungen um den Zeitpunkt des Ablaufs des Changes-Tokens. Der Zeitraum ist jedoch kürzer und kann einige Stunden bis einige Tage dauern.
    • Speichern des Zeitstempels des letzten Lesens von Daten aus Health Connect.
    • Lesen Sie nach Ablauf des Tokens alle Daten ab diesem Zeitstempel.
  • Daten der letzten 30 Tage löschen und dann lesen: Das entspricht eher dem, was bei der ersten Integration passiert.
    • Alle Daten löschen, die von der App in den letzten 30 Tagen aus Health Connect gelesen wurden.
    • Lies alle diese Daten noch einmal, nachdem du sie gelöscht hast.
  • Lesen von Daten der letzten 30 Tage ohne Deduplizierung: Das ist die am wenigsten ideale Strategie, da Nutzern doppelte Daten angezeigt werden.
    • Alle Daten löschen, die von der App in den letzten 30 Tagen aus Health Connect gelesen wurden.
    • Doppelte Einträge zulassen.

Tokens für Änderungen des Datentyps

Wenn Ihre App mehrere Datentypen unabhängig voneinander verwendet, verwenden Sie für jeden Datentyp separate Changes-Tokens. 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 Menge an Daten 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 Sie die Berechtigung Background Read anfordern, kann der Nutzer Ihrer App Zugriff zum Lesen von Daten im Hintergrund gewähren.

Importzeitangaben

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, während Ihre App im Vordergrund ausgeführt wird. Nutzer benachrichtigen, wenn neue Daten verfügbar sind, damit sie ihren Bildschirm aktualisieren können, um die Änderungen zu sehen.