Zapisywanie danych

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:

  1. Częstotliwość zapisywania: jak często aplikacja przesyła nowe dane do Health Connect. Na przykład zapisywać nowe dane co 15 minut.
  2. 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%