Ten przewodnik jest zgodny z Health Connect w wersji 1.1.0-alpha12.
W tym przewodniku omówiliśmy proces zapisywania i aktualizowania danych w Health Connect.
Obsługa wartości zero
Niektóre typy danych, np. liczba kroków, odległość czy spalone kalorie, mogą mieć wartość 0
.
Zapisuj wartości 0 tylko wtedy, gdy odzwierciedlają one prawdziwą nieaktywność podczas noszenia urządzenia przez użytkownika. Nie wpisuj wartości zerowych, jeśli urządzenie nie było noszone, brakuje danych lub bateria jest rozładowana. W takich przypadkach pomiń rekord, aby uniknąć wprowadzania użytkowników w błąd.
Konfigurowanie struktury danych
Zanim zapiszemy dane, musimy najpierw skonfigurować rekordy. Istnieje ponad 50 typów danych, z których każdy ma swoją strukturę. Więcej informacji o dostępnych typach danych znajdziesz w dokumentacji Jetpacka.
Podstawowe rekordy
Typ danych Kroki w Health Connect rejestruje liczbę kroków wykonanych przez użytkownika między odczytami. Licznik kroków to powszechny pomiar na platformach związanych ze zdrowiem, kondycją i dobrym samopoczuciem.
Ten przykład pokazuje, jak ustawić dane dotyczące liczby kroków:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
Rekordy z jednostkami miary
Health Connect może przechowywać wartości wraz z jednostkami miary, aby zapewnić dokładność. Przykładem jest typ danych Nutrition, który jest obszerny i wszechstronny. Obejmuje ona wiele opcjonalnych pól składników odżywczych, od węglowodanów po witaminy. Każdy punkt danych przedstawia składniki odżywcze, które mogły zostać spożyte w ramach posiłku lub produktu spożywczego.
W tym typie danych wszystkie składniki odżywcze są reprezentowane w jednostkach Mass
, a energy
– w jednostkach Energy
.
Ten przykład pokazuje, jak ustawić dane dotyczące wartości odżywczej dla użytkownika, który zjadł banana:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(1))
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 = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
device = Device(type = Device.TYPE_PHONE)
)
)
Rekordy z danymi serii
Health Connect może przechowywać listę danych serii. Przykładem jest typ danych Tętno, który rejestruje serię próbek bicia serca wykrytych między odczytami.
W przypadku tego typu danych parametr samples
jest reprezentowany przez listę przykładów tętna. Każda próbka zawiera wartość beatsPerMinute
i wartość time
.
Ten przykład pokazuje, jak ustawić dane serii tętna:
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
val heartRateRecord = HeartRateRecord(
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
// records 10 arbitrary data, to replace with actual data
samples = List(10) { index ->
HeartRateRecord.Sample(
time = startTime + Duration.ofSeconds(index.toLong()),
beatsPerMinute = 100 + index.toLong(),
)
},
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
))
Prośba o przyznanie uprawnień przez użytkownika
Po utworzeniu instancji klienta aplikacja musi poprosić użytkownika o przyznanie uprawnień. Użytkownicy muszą mieć możliwość przyznania lub odmowy przyznania uprawnień w dowolnym momencie.
Aby to zrobić, utwórz zestaw uprawnień dla wymaganych typów danych. Najpierw sprawdź, czy uprawnienia w zestawie są zadeklarowane w pliku manifestu Androida.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
HealthPermission.getReadPermission(HeartRateRecord::class),
HealthPermission.getWritePermission(HeartRateRecord::class),
HealthPermission.getReadPermission(StepsRecord::class),
HealthPermission.getWritePermission(StepsRecord::class)
)
Użyj getGrantedPermissions
, aby sprawdzić, czy Twoja aplikacja ma już przyznane wymagane uprawnienia. Jeśli nie, użyj createRequestPermissionResultContract
, aby poprosić o te uprawnienia. Wyświetli się ekran uprawnień Health Connect.
// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
if (granted.containsAll(PERMISSIONS)) {
// Permissions successfully granted
} else {
// Lack of required permissions
}
}
suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
val granted = healthConnectClient.permissionController.getGrantedPermissions()
if (granted.containsAll(PERMISSIONS)) {
// Permissions already granted; proceed with inserting or reading data
} else {
requestPermissions.launch(PERMISSIONS)
}
}
Użytkownicy mogą w dowolnym momencie przyznać lub wycofać uprawnienia, dlatego aplikacja musi okresowo sprawdzać, czy zostały one przyznane, oraz obsługiwać scenariusze, w których użytkownik je wycofuje.
Zapisywanie danych
Jednym z częstych procesów w Health Connect jest zapisywanie danych. Aby dodać rekordy, użyj insertRecords
.
Ten przykład pokazuje, jak zapisać liczbę kroków wstawiania danych:
suspend fun insertSteps(healthConnectClient: HealthConnectClient) {
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(5))
try {
val stepsRecord = StepsRecord(
count = 120,
startTime = startTime,
endTime = endTime,
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.autoRecorded(
device = Device(type = Device.TYPE_WATCH)
)
)
healthConnectClient.insertRecords(listOf(stepsRecord))
} catch (e: Exception) {
// Run error handling here
}
}
Aktualizowanie danych
Jeśli chcesz zmienić co najmniej 1 rekord, zwłaszcza gdy musisz zsynchronizować bazę danych aplikacji z danymi z Health Connect, możesz zaktualizować swoje dane. Istnieją 2 sposoby aktualizowania dotychczasowych danych. Zależy to od identyfikatora używanego do znajdowania rekordów.
Metadane
Warto najpierw zbadać klasę Metadata
, ponieważ jest to konieczne podczas aktualizowania danych. W momencie utworzenia każda Record
w Health Connect ma pole metadata
. W przypadku synchronizacji istotne są te właściwości:
Właściwości | Opis |
---|---|
id
|
Każdy Record w Health Connect ma unikalną wartość id .Health Connect automatycznie wypełnia to pole podczas wstawiania nowego rekordu. |
lastModifiedTime
|
Każdy rekord Record zawiera też informację o dacie ostatniej modyfikacji.Health Connect wypełnia to automatycznie. |
clientRecordId
|
Każdy element Record może mieć powiązany unikalny identyfikator, który służy jako odniesienie w danych aplikacji.
Tę wartość podaje aplikacja. |
clientRecordVersion
|
Jeśli rekord zawiera element clientRecordId , możesz użyć elementu clientRecordVersion , aby dane były zsynchronizowane z wersją w puli danych aplikacji.Tę wartość podaje aplikacja. |
Aktualizacja po przeczytaniu według zakresu czasowego
Aby zaktualizować dane, najpierw przygotuj potrzebne rekordy. W razie potrzeby wprowadź zmiany w rekordach. Następnie wywołaj updateRecords
, aby wprowadzić zmiany.
Ten przykład pokazuje, jak zaktualizować dane. W tym celu wartości przesunięcia strefy dla każdego rekordu są dostosowywane do czasu 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)
}
healthConnectClient.updateRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
Wgrywanie i aktualizowanie za pomocą identyfikatora rekordu klienta
Jeśli używasz opcjonalnych wartości identyfikatora rekordu klienta i wersji rekordu klienta, zalecamy użycie wartości insertRecords
zamiast updateRecords
.
Funkcja insertRecords
umożliwia upsertowanie danych.
Jeśli dane istnieją w Health Connect na podstawie podanego zbioru identyfikatorów rekordów klienta, zostaną zastąpione. W przeciwnym razie są one zapisywane jako nowe dane.
Ten scenariusz jest przydatny, gdy chcesz zsynchronizować dane z danych aplikacji z Health Connect.
Ten przykład pokazuje, jak wykonać operację upsert na danych pobranych z danych aplikacji:
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(
metadata = Metadata.autoRecorded(
clientRecordId = "Your client record ID",
device = Device(type = Device.TYPE_WATCH)
),
// Assign more parameters for this record
)
appStepsRecords.add(sr)
// ...
return appStepsRecords
}
suspend fun upsertSteps(
healthConnectClient: HealthConnectClient,
newStepsRecords: ArrayList<StepsRecord>
) {
try {
healthConnectClient.insertRecords(newStepsRecords)
} catch (e: Exception) {
// Run error handling here
}
}
Następnie możesz wywoływać te funkcje w głównym wątku.
upsertSteps(healthConnectClient, pullStepsFromDatastore())
Sprawdzanie wartości w wersji rekordu klienta
Jeśli proces aktualizacji danych obejmuje wersję rekordu klienta, Health Connect przeprowadza porównania w wartościach clientRecordVersion
. Jeśli wersja z wstawionych danych jest wyższa niż wersja z dotychczasowych danych, następuje aktualizacja. W przeciwnym razie proces zignoruje zmianę, a wartość pozostanie taka sama.
Aby uwzględnić wersjonowanie w danych, musisz podać wartość Metadata.clientRecordVersion
z wartością Long
określoną na podstawie logiki wersji.
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofMinutes(15))
val stepsRecord = StepsRecord(
count = 100L,
startTime = startTime,
startZoneOffset = ZoneOffset.UTC,
endTime = endTime,
endZoneOffset = ZoneOffset.UTC,
metadata = Metadata.manualEntry(
clientRecordId = "Your supplied record ID",
clientRecordVersion = 0L, // Your supplied record version
device = Device(type = Device.TYPE_WATCH)
)
)
Aktualizacje nie są automatycznie zwiększane o version
, gdy występują zmiany, co zapobiega nieoczekiwanym przypadkom nadpisywania danych. W tym przypadku musisz ręcznie podać wyższą wartość.
Sprawdzone metody zapisywania danych
Aplikacje mogą zapisywać w Health Connect tylko dane pochodzące z własnych źródeł.
Jeśli dane w Twojej aplikacji zostały zaimportowane z innej aplikacji, to ta druga aplikacja jest odpowiedzialna za zapisywanie własnych danych w Health Connect.
Warto też zaimplementować logikę, która obsługuje wyjątki podczas zapisu, takie jak dane spoza zakresu lub błąd wewnętrzny systemu. Możesz zastosować swoje strategie wycofywania i ponownego próbowania w mechanizmie planowania zadań. Jeśli zapisywanie danych w Health Connect nie powiedzie się, sprawdź, czy aplikacja może przejść przez ten punkt eksportu. Aby ułatwić diagnozę, nie zapomnij odnotować i zgłosić błędów.
Podczas śledzenia danych możesz postępować zgodnie z kilkoma sugestiami, w zależności od sposobu zapisywania danych przez aplikację.
Obsługa strefy czasowej
Podczas zapisywania rekordów opartych na czasie unikaj domyślnego ustawiania przesunięć zoneOffset.UTC, ponieważ może to spowodować niedokładne sygnatury czasowe, gdy użytkownicy znajdują się w innych strefach czasowych. Zamiast tego oblicz przesunięcie na podstawie rzeczywistej lokalizacji urządzenia.
Strefa czasowa urządzenia może zostać pobrana za pomocą ZoneId.systemDefault()
.
val endTime = Instant.now()
val startTime = endTime.minus(java.time.Duration.ofDays(1))
val stepsRecords = mutableListOf<StepsRecord>()
var sampleTime = startTime
val minutesBetweenSamples = 15L
while (sampleTime < endTime) {
// Get the default ZoneId then convert it to an offset
val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(sampleTime)
stepsRecords += StepsRecord(
startTime = sampleTime.minus(java.time.Duration.ofMinutes(minutesBetweenSamples)),
startZoneOffset = zoneOffset,
endTime = sampleTime,
endZoneOffset = zoneOffset,
count = Random.nextLong(1, 100),
metadata = Metadata.unknownRecordingMethod(),
)
sampleTime = sampleTime.plus(java.time.Duration.ofMinutes(minutesBetweenSamples))
}
healthConnectClient.insertRecords(
stepsRecords
)
Aby dowiedzieć się więcej, zapoznaj się z dokumentacją ZoneId
.
Śledzenie pasywne
Dotyczy to również aplikacji, które wykonują pasywne śledzenie aktywności fizycznej lub stanu zdrowia, np. ciągłe rejestrowanie kroków lub tętna w tle.
Aplikacja musi okresowo zapisywać dane w Health Connect w jeden z tych sposobów:
- Podczas każdej synchronizacji zapisuj tylko nowe dane i zaktualizowane dane, które zostały zmodyfikowane od czasu ostatniej synchronizacji.
- Podziel żądania na maksymalnie 1000 rekordów na żądanie zapisu.
- Użyj polecenia
WorkManager
, aby zaplanować okresowe zadania w tle z okresem co najmniej 15 minut. - Ogranicz zadania do działania tylko wtedy, gdy urządzenie jest nieaktywne i nie ma niskiego poziomu naładowania baterii.
```kotlin
val constraints = Constraints.Builder()
.requiresBatteryNotLow()
.requiresDeviceIdle(true)
.build()
val writeDataWork = PeriodicWorkRequestBuilder<WriteDataToHealthConnectWorker>(
15,
TimeUnit.MINUTES,
5,
TimeUnit.MINUTES
)
.setConstraints(constraints)
.build()
```
Aktywna lokalizacja
Dotyczy to aplikacji, które śledzą dane na podstawie zdarzeń, takich jak ćwiczenia i sen, lub danych wprowadzanych ręcznie przez użytkownika, np. dotyczących odżywiania. Te rekordy są tworzone, gdy aplikacja jest na pierwszym planie, lub w rzadkich przypadkach, gdy jest używana kilka razy dziennie.
Upewnij się, że aplikacja nie utrzymuje Health Connect w stanie działania przez cały czas trwania zdarzenia.
Dane muszą być zapisywane w Health Connect w jeden z 2 sposobów:
- Po zakończeniu wydarzenia zsynchronizuj dane z Health Connect. Na przykład zsynchronizuj dane, gdy użytkownik zakończy śledzenie sesji ćwiczeń.
- Zaplanuj jednorazowe zadanie za pomocą
WorkManager
, aby zsynchronizować dane w późniejszym terminie.
Sprawdzone metody dotyczące szczegółowości i częstotliwości zapisów
Podczas zapisywania danych w Health Connect używaj odpowiedniej rozdzielczości. Korzystanie z odpowiedniej rozdzielczości pomaga zmniejszyć obciążenie pamięci, a jednocześnie zachować spójne i dokładne dane. Rozdzielczość danych obejmuje 2 elementy:
- Częstotliwość zapisywania: jak często aplikacja przesyła nowe dane do Health Connect. Na przykład zapisywać nowe dane co 15 minut.
- Szczegółowość zapisywanych danych: jak często dane, które są przesyłane, były próbkowane. Na przykład zapisuj próbki tętna co 5 s. Nie wszystkie typy danych wymagają tej samej częstotliwości próbkowania. Aktualizowanie danych o liczbie kroków co sekundę nie przynosi wielu korzyści w porównaniu z rzadszym odświeżaniem, np. co 60 sekund. Jednak wyższe częstotliwości próbkowania mogą zapewnić użytkownikom bardziej szczegółowy wgląd w ich dane dotyczące zdrowia i kondycji. Częstotliwości próbkowania powinny zapewniać równowagę między szczegółowością a wydajnością.
Dane zapisywane w ciągu dnia
W przypadku danych zbieranych w ciągłym trybie, takich jak liczba kroków, aplikacja powinna zapisywać dane w Health Connect co najmniej co 15 minut w ciągu dnia.
Typ danych |
Jednostka |
Oczekiwane |
Przykład |
Kroki |
kroki |
Co minutę |
23:14 – 23:15 – 5 kroków 23:16 - 23:17 - 22 kroki 23:17 – 23:18 – 8 krok |
Kadencja kroków |
kroki/min |
Co minutę |
23:14 – 23:15 – 5 km/h 23:16 – 23:17 – 22 spm 23:17 – 23:18 – 8 spm |
Pchnięcia wózka inwalidzkiego |
naciśnięcia |
Co minutę |
23:14 – 23:15 – 5 push 23:16 – 23:17 – 22 pushe 23:17 – 23:18 – 8 kliknięć |
ActiveCaloriesBurned |
Kalorie |
Co 15 minut |
23:15 – 23:30 – 2 kalorie 23:30 – 23:45 – 25 kalorii 23:45 – 00:00 – 5 kalorii |
TotalCaloriesBurned |
Kalorie |
Co 15 minut |
23:15 – 23:30 – 16 kalorii 23:30 – 23:45 – 16 kalorii 23:45 – 00:00 – 16 kalorii |
Dystans |
km/min |
Co minutę |
23:14–23:15 – 0,008 km 23:16 – 23:16 – 0,021 km 23:17 – 23:18 – 0,012 km |
Przebyte przewyższenie |
min |
Co minutę |
20:36 - 20:37 - 3,048 m 20:39 – 20:40 – 3,048 m 23:23 – 23:24 – 9,144 m |
Zdobyte piętra |
piętra |
Co minutę |
23:14 – 23:15 – 5 pięter 23:16 – 23:16 – 22 piętra 23:17 – 23:18 – 8 pięter |
HeartRate |
uderz./min |
4 razy na minutę |
6:11:15 rano – 55 uderzeń na minutę 6:11:30 rano – 56 uderzeń na minutę 6:11:45 rano – 56 uderzeń na minutę 6:12:00 rano – 55 uderzeń na minutę |
HeartRateVariabilityRmssd |
ms |
Co minutę |
6:11 rano – 23 ms |
RespiratoryRate |
oddechów/min |
Co minutę |
23:14 – 23:15 – 60 oddechów na minutę 23:16 - 23:16 - 62 oddechów/minutę 23:17 – 23:18 – 64 oddechy na minutę |
OxygenSaturation |
% |
Co godzinę |
6:11 – 95,208% |
Zapisuj sesje
Dane powinny być zapisywane w Health Connect po zakończeniu treningu lub sesji snu.
Zgodnie ze sprawdzoną metodą każda sesja snu lub ćwiczeń powinna być zapisywana za pomocą urządzenia do nagrywania i odpowiednich metadanych.
Aplikacja powinna być zgodna z zaleceniami podanymi w kolumnie „Oczekiwane” poniżej. W miarę możliwości postępuj zgodnie z „najlepszymi” wskazówkami.
Dane śledzone podczas ćwiczeń
Typ danych |
Jednostka |
Oczekiwane |
Pozdrawiam |
Przykład |
Kroki |
kroki |
Co minutę |
Co sekundę |
23:14-23:15 – 5 kroków 23:16 - 23:17 - 22 kroki 23:17 – 23:18 – 8 krok |
Kadencja kroków |
kroki/min |
Co minutę |
Co sekundę |
23:14-23:15 – 35 spm 23:16 - 23:17 - 37 spm 23:17 – 23:18 – 40 spm |
Pchnięcia wózka inwalidzkiego |
naciśnięcia |
Co minutę |
Co sekundę |
23:14–23:15 – 5 kliknięć 23:16 – 23:17 – 22 pushe 23:17 – 23:18 – 8 kliknięć |
CyclingPedalingCadence |
obr./min |
Co minutę |
Co sekundę |
23:14–23:15 – 65 obr./min 23:16 - 23:17 - 70 obr./min 23:17 – 23:18 – 68 obr./min |
Moc |
waty |
Co minutę |
Co sekundę |
23:14-23:15 – 250 W 23:16 – 23:17 – 255 W 23:17 – 23:18 – 245 W |
Szybkość |
km/min |
Co minutę |
Co sekundę |
23:14-23:15 – 0,3 km/min 23:16 – 23:17 – 0,4 km/min 23:17 – 23:18 – 0,4 km/min |
Dystans |
km/m |
Co minutę |
Co sekundę |
23:14–23:15 – 0,008 km 23:16 – 23:16 – 0,021 km 23:17 – 23:18 – 0,012 km |
ActiveCaloriesBurned |
Kalorie |
Co minutę |
Co sekundę |
23:14-23:15 – 20 kalorii 23:16 – 23:17 – 20 kalorii 23:17 – 23:18 – 25 kalorii |
TotalCaloriesBurned |
Kalorie |
Co minutę |
Co sekundę |
23:14-23:15 – 36 kalorii 23:16 – 23:17 – 36 kalorii 23:17 – 23:18 – 41 kalorii |
Przebyte przewyższenie |
min |
Co minutę |
Co sekundę |
20:36 - 20:37 - 3,048 m 20:39 – 20:40 – 3,048 m 23:23 – 23:24 – 9,144 m |
ExerciseRoutes |
lat/lng/alt |
Co 3–5 sekund |
Co sekundę |
|
HeartRate |
uderz./min |
4 razy na minutę |
Co sekundę |
23:14–23:15 – 150 uderzeń na minutę |
Dane gromadzone podczas snu
Typ danych |
Jednostka |
Oczekiwane próbki |
Przykład |
Etapy snu |
etap |
szczegółowy okres w poszczególnych fazach snu, |
23:46 – 23:50 – przebudzenie 23:50 – 23:56 – sen płytki 23:56 – 00:16 – sen głęboki |
RestingHeartRate |
uderz./min |
pojedyncza wartość dzienna (oczekuje się ją o poranku); |
6:11 godz. – 60 uderzeń na minutę |
OxygenSaturation |
% |
pojedyncza wartość dzienna (oczekuje się ją o poranku); |
6:11 – 95,208% |