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:
- Obtain a list of new or updated entries from your app's datastore.
- For each entry, create a
Record
object appropriate for that data type. For example, create aWeightRecord
object for data related to weight. Specify a
Metadata
object with eachRecord
using the unique key and version details from your app's datastore. If your data is not versioned, you can use theLong
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 )
Upsert data to Health Connect using
insertRecords
. Upserting data means that any existing data in Health Connect gets overwritten as long as theclientRecordId
values exist in the Health Connect datastore, and theclientRecordVersion
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:
- Call
getChanges
using the token to obtain a list of Changes. - Check each change whether its type of change is an
UpsertionChange
or aDeletionChange
, 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.
- For
- Assign the next Changes token as your new token.
- Repeat Steps 1-3 until there are no Changes left.
- 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 record type and list of id
and clientRecordId
values, which makes it convenient to batch multiple data for deletion. An
alternative deleteRecords
that takes in a timeRangeFilter
is also available.
Best practices for syncing data
The following factors affect the syncing process.
Token expiration
Since an unused Changes token expires within 30 days, you must use a sync strategy that avoids losing information in such a case. Your strategy could include the following approaches:
- Search your app datastore for the most recently consumed record that also
has an
id
from Health Connect. - Request records from Health Connect that begin with a specific timestamp, and then insert or update them in your app's datastore.
- Request a Changes token to reserve it for the next time it's needed.
Recommended Changes management strategies
In case your app is getting invalid or expired Changes tokens, we recommend the following management strategies depending on its application in your logic:
- Read and dedupe all data. This is the most ideal strategy.
- Store the timestamp of the last time they read data from Health Connect.
- On token expiry, re-read all data from the most recent timestamp or for the last 30 days. Then, dedupe it against the previously read data using identifiers.
- Ideally, implement Client IDs since they are required for data updates.
- Only read data since the last read timestamp. This results in some data
discrepancies around the time of Changes token expiry, but the time period
is shorter that could take a few hours to a couple of days.
- Store the timestamp of the last time they read data from Health Connect.
- On token expiry, read all data from this timestamp onwards.
- Delete then read data for the last 30 days. This aligns more closely
with what happens on the first integration.
- Delete all data read by the app from Health Connect for the last 30 days.
- Once deleted, read all of this data again.
- Read data for last 30 days without deduping. This is the least ideal
strategy, and results in having duplicate data displayed to users.
- Delete all data read by the app from Health Connect for the last 30 days.
- Allow duplicate entries.
Data type Changes tokens
If your app consumes more than one data type independently, use separate Changes Tokens for each data type. Only use a list of multiple data types with the Changes Sync API if these data types are either consumed together or not at all.
Foreground reads
Apps can only read data from Health Connect while they are in the foreground. When syncing data from Health Connect, access to Health Connect may be interrupted at any point. For example, your app must handle interruptions midway through a sync when reading a large amount of data from Health Connect, and continue the next time the app is opened.
Import timings
As your app can't get notified of new data, check for new data at two points:
- Each time your app becomes active in the foreground. In this case, use lifecycle events.
- Periodically, while your app remains in the foreground. Notify users when new data is available, allowing them to update their screen to reflect the changes.