This guide covers the process of writing or updating data in Health Connect.
Set up data structure
Before writing data, we need to set up the records first. For more than 50 data types, each have their respective structures. See the Jetpack reference for more details about the data types available.
Basic records
The Steps data type in Health Connect captures the number of steps a user has taken between readings. Step counts represent a common measurement across health, fitness, and wellness platforms.
The following example shows how to set steps count data:
val stepsRecord = StepsRecord(
count = 120,
startTime = START_TIME,
endTime = END_TIME,
startZoneOffset = START_ZONE_OFFSET,
endZoneOffset = END_ZONE_OFFSET
)
Records with units of measurement
Health Connect can store values along with their units of measurement to provide accuracy. One example is the Nutrition data type that is vast and comprehensive. It includes a wide variety of optional nutrient fields ranging from total carbohydrates to vitamins. Each data point represents the nutrients that were potentially consumed as part of a meal or food item.
In this data type, all of the nutrients are represented in units of
Mass
, while energy
is represented in a unit of Energy
.
The following example shows how to set nutrition data for a user who has eaten a banana:
val banana = NutritionRecord(
name = "banana",
energy = 105.0.kilocalories,
dietaryFiber = 3.1.grams,
potassium = 0.422.grams,
totalCarbohydrate = 27.0.grams,
totalFat = 0.4.grams,
saturatedFat = 0.1.grams,
sodium = 0.001.grams,
sugar = 14.0.grams,
vitaminB6 = 0.0005.grams,
vitaminC = 0.0103.grams,
startTime = START_TIME,
endTime = END_TIME,
startZoneOffset = START_ZONE_OFFSET,
endZoneOffset = END_ZONE_OFFSET
)
Records with series data
Health Connect can store a list of series data. One example is the Heart Rate data type that captures a series of heartbeat samples detected between readings.
In this data type, the parameter samples
is represented by a list of
Heart Rate samples. Each sample contains a beatsPerMinute
value and a time
value.
The following example shows how to set heart rate series data:
val heartRateRecord = HeartRateRecord(
startTime = START_TIME,
startZoneOffset = START_ZONE_OFFSET,
endTime = END_TIME,
endZoneOffset = END_ZONE_OFFSET,
// records 10 arbitrary data, to replace with actual data
samples = List(10) { index ->
HeartRateRecord.Sample(
time = START_TIME + Duration.ofSeconds(index.toLong()),
beatsPerMinute = 100 + index.toLong(),
)
}
)
Write data
One of the common workflows in Health Connect is writing data. To add records,
use insertRecords
.
The following example shows how to write data inserting step counts:
suspend fun insertSteps(healthConnectClient: HealthConnectClient) {
try {
val stepsRecord = StepsRecord(
count = 120,
startTime = START_TIME,
endTime = END_TIME,
startZoneOffset = START_ZONE_OFFSET,
endZoneOffset = END_ZONE_OFFSET
)
healthConnectClient.insertRecords(listOf(stepsRecord))
} catch (e: Exception) {
// Run error handling here
}
}
Update data
If you need to change one or more records, especially when you need to sync your app datastore with data from Health Connect, you can update your data. There are two ways to update existing data which depends on the identifier used to find records.
Metadata
It is worth examining the Metadata
class first as this is necessary when
updating data. On creation, each Record
in Health Connect has a
metadata
field. The following properties are relevant to
synchronization:
Properties | Description |
---|---|
id
|
Every Record in Health Connect has a unique id
value.Health Connect automatically populates this when inserting a new record. |
lastModifiedTime
|
Every Record also keeps track of the last time the
record was modified.Health Connect automatically populates this. |
clientRecordId
|
Each Record can have a unique ID associated with
it to serve as reference in your app datastore.
Your app supplies this value. |
clientRecordVersion
|
Where a record has clientRecordId , the
clientRecordVersion can be used to allow data to
stay in sync with the version in your app
datastore.Your app supplies this value. |
Update through Record ID
To update data, prepare the needed records first. Perform any changes to the
records if necessary. Then, call updateRecords
to make
the changes.
The following example shows how to update data. For this purpose, each record has its zone offset values adjusted into PST.
suspend fun updateSteps(
healthConnectClient: HealthConnectClient,
prevRecordStartTime: Instant,
prevRecordEndTime: Instant
) {
try {
val request = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = StepsRecord::class,
timeRangeFilter = TimeRangeFilter.between(
prevRecordStartTime,
prevRecordEndTime
)
)
)
val newStepsRecords = arrayListOf<StepsRecord>()
for (record in request.records) {
// Adjusted both offset values to reflect changes
val sr = StepsRecord(
count = record.count,
startTime = record.startTime,
startZoneOffset = record.startTime.atZone(ZoneId.of("PST")).offset,
endTime = record.endTime,
endZoneOffset = record.endTime.atZone(ZoneId.of("PST")).offset,
metadata = record.metadata
)
newStepsRecords.add(sr)
}
client.updateRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
Upsert through Client Record ID
If you are using the optional Client Record ID and Client Record Version values,
we recommend using insertRecords
instead of updateRecords
.
The insertRecords
function has the ability to upsert data.
If the data exists in Health Connect based on the given set of
Client Record IDs, it gets overwritten. Otherwise, it is written as new data.
This scenario is useful whenever you need to sync data from
your app datastore to Health Connect.
The following example shows how to perform an upsert on data pulled from the app datastore:
suspend fun pullStepsFromDatastore() : ArrayList<StepsRecord> {
val appStepsRecords = arrayListOf<StepsRecord>()
// Pull data from app datastore
// ...
// Make changes to data if necessary
// ...
// Store data in appStepsRecords
// ...
var sr = StepsRecord(
// Assign parameters for this record
metadata = Metadata(
clientRecordId = cid
)
)
appStepsRecords.add(sr)
// ...
return appStepsRecords
}
suspend fun upsertSteps(
healthConnectClient: HealthConnectClient,
newStepsRecords: ArrayList<StepsRecord>
) {
try {
healthConnectClient.insertRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
After that, you can call these functions in your main thread.
upsertSteps(healthConnectClient, pullStepsFromDatastore())
Value check in Client Record Version
If your process of upserting data includes the Client Record Version, Health
Connect performs comparison checks in the clientRecordVersion
values. If the version from the inserted data is higher than the
version from the existing data, the upsert happens. Otherwise, the process
ignores the change and the value remains the same.
To include versioning in your data, you need to supply
Metadata.clientRecordVersion
with a Long
value based on your versioning
logic.
val sr = StepsRecord(
count = count,
startTime = startTime,
startZoneOffset = startZoneOffset,
endTime = endTime,
endZoneOffset = endZoneOffset,
metadata = Metadata(
clientRecordId = cid,
clientRecordVersion = version
)
)
Upserts don't automatically increment version
whenever there are changes,
preventing any unexpected instances of overwriting data. With that, you have to
manually supply it with a higher value.
Best practices
Once you have constructed the logic, consider following the best practices when writing or updating data.