Synchronize data

Most apps that integrate with Health Connect have their own datastore that serves as the source of truth. Health Connect provides ways to keep your app in sync.

Make sure your app does the following:

  • Feeds new or updated data from your app's datastore to Health Connect.
  • Pulls data changes from Health Connect, which are reflected in your app's datastore.
  • Deletes data from Health Connect when it's deleted in your app's datastore.

In each case, make sure that the syncing process keeps both Health Connect and your app's datastore aligned.

Feed data to Health Connect

The first part of the syncing process is to feed data from your app's datastore to the Health Connect datastore.

Prepare your data

Usually, records in your app's datastore have the following details:

  • A unique key, such as a UUID.
  • A version or timestamp.

Design your app's datastore to keep track of what data has already been fed to Health Connect. To achieve this, apply the following logic:

  • Provide a list of changes and a token that can be used to retrieve records that have updates since the last token was issued.
  • Track the last time the exported data was modified.

These steps are essential to ensure that only new or updated data is fed to Health Connect.

Write data to Health Connect

To feed data into Health Connect, carry out the following steps:

  1. Obtain a list of new or updated entries from your app's datastore.
  2. For each entry, create a Record object appropriate for that data type. For example, create a WeightRecord object for data related to weight.
  3. Specify a Metadata object with each Record using the unique key and version details from your app's datastore. If your data is not versioned, you can use the Long value of the current timestamp as an alternative.

    val record = WeightRecord(
        metadata = Metadata(
            clientRecordId = "<Your record's Client ID>",
            clientRecordVersion = <Your record's version>
        ),
        weight = weight,
        time = time,
        zoneOffset = zoneOffset
    )
    
  4. Upsert data to Health Connect using insertRecords. Upserting data means that any existing data in Health Connect gets overwritten as long as the clientRecordId values exist in the Health Connect datastore, and the clientRecordVersion is higher than the existing value. Otherwise, the upserted data is written as new data.

    healthConnectClient.insertRecords(arrayListOf(record))
    

To learn about the practical considerations for feeding data, check out the best practices for Write data.

Store Health Connect IDs

After upserting your records to Health Connect, your app's datastore needs to store the Health Connect id for each record. This allows your app to check if each incoming change requires creating a new record, or updating an existing record, after you pull the data.

The insertRecords function returns a InsertRecordsResponse that contains the list of id values. Use the response to get the Record IDs and store them.

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

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

Pull data from Health Connect

The second part of the syncing process is to pull for any data changes from Health Connect to your app's datastore. The data changes can include updates and deletions.

Get a Changes token

To get a list of changes to pull from Health Connect, your app needs to keep track of Changes tokens. You can use them when requesting Changes to return both a list of data changes, and a new Changes token to be used next time.

To obtain a Changes token, call getChangesToken and supply the required data types.

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

Check for data changes

Now that you've obtained a Changes token, use it to get all Changes. We recommend creating a loop to get through all the Changes where it checks if there are available data changes. Here are the following steps:

  1. Call getChanges using the token to obtain a list of Changes.
  2. Check each change whether its type of change is an UpsertionChange or a DeletionChange, and perform the necessary operations.
    • For UpsertionChange, only take changes that didn't come from the calling app to make sure you're not re-importing data.
  3. Assign the next Changes token as your new token.
  4. Repeat Steps 1-3 until there are no Changes left.
  5. Store the next token and reserve it for a future 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
}

To learn about the practical considerations for pulling data, check out the best practices for Sync data.

Process data changes

Reflect the changes to your app's datastore. For UpsertionChange, use the id and the lastModifiedTime from its metadata to upsert the record. For DeletionChange, use the id provided to delete the record.

Delete data from Health Connect

When a user deletes their own data from your app, make sure that the data is also removed from Health Connect. Use deleteRecords to do this. This takes a list of id and clientRecordId values, which makes it convenient to batch multiple data for deletion.