Jeśli chcesz utworzyć w aplikacji jakość treningu, możesz użyć Health Connect do takich czynności jak:
- zapisywanie sesji ćwiczeń,
- zapisywanie tras treningów,
- zapisywanie wskaźników treningowych, takich jak tętno, prędkość i dystans,
- odczytywanie danych treningowych z innych aplikacji.
Z tego przewodnika dowiesz się, jak tworzyć te funkcje treningowe, w tym jak korzystać z typów danych, wykonywania w tle, uprawnień, zalecanych przepływów pracy i sprawdzonych metod.
Omówienie: tworzenie kompleksowego trackera treningów
Aby utworzyć kompleksową funkcję śledzenia treningów za pomocą Health Connect, wykonaj te podstawowe czynności:
- Prawidłowo wdroż uprawnienia na podstawie uprawnień związanych ze zdrowiem.
- Rejestruj sesje za pomocą
ExerciseSessionRecord. - Zapisuj dane treningowe w spójny sposób podczas sesji.
- Prawidłowo zarządzaj wykonywaniem w tle, aby mieć pewność, że dane są rejestrowane w sposób ciągły.
- Odczytuj dane sesji na potrzeby podsumowań i analiz po treningu.
Ten przepływ pracy umożliwia interoperacyjność z innymi aplikacjami Health Connect i weryfikuje dostęp do danych kontrolowany przez użytkownika.
Zanim zaczniesz
Zanim wdrożysz funkcje treningowe:
- Zintegruj Health Connect za pomocą odpowiedniej zależności.
- Utwórz instancję
HealthConnectClient. - Sprawdź, czy Twoja aplikacja implementuje przepływy uprawnień na czas działania na podstawie uprawnień związanych ze zdrowiem.
- Jeśli Twój przepływ pracy korzysta z GPS-a, skonfiguruj dostęp do lokalizacji i usługę na pierwszym planie.
Kluczowych pojęć
Health Connect reprezentuje dane treningowe za pomocą kilku podstawowych komponentów. ExerciseSessionRecord pełni funkcję centralnego rekordu treningu, zawierającego szczegóły takie jak czas rozpoczęcia i zakończenia oraz typ ćwiczenia. Podczas sesji można rejestrować różne typy danych, takie jak HeartRateRecord czy SpeedRecord. W przypadku aktywności na świeżym powietrzu ExerciseRoute przechowuje dane GPS, które są powiązane z odpowiednią sesją.
Sesje ćwiczeń
ExerciseSessionRecord to centralny rekord danych treningowych, który reprezentuje pojedynczą sesję treningową. Każdy rekord przechowuje:
startTimeendTimeexerciseType- opcjonalne metadane sesji (tytuł, notatki).
ExerciseSessionRecord może też zawierać trasy ćwiczeń, okrążenia i segmenty. Ponadto podczas sesji można rejestrować inne typy danych, takie jak HeartRateRecord czy SpeedRecord, i powiązywać je z sesją.
Powiązane typy danych
Dane powiązane z sesjami treningowymi są reprezentowane przez poszczególne typy rekordów. Typowe typy to:
HeartRateRecord: reprezentuje serię pomiarów tętna.SpeedRecord: reprezentuje serię pomiarów prędkości.DistanceRecord: reprezentuje odległość pokonaną między odczytami.TotalCaloriesBurnedRecord: reprezentuje łączną liczbę spalonych kalorii między odczytami.ElevationGainedRecord: reprezentuje wysokość zdobytą między odczytami.StepsCadenceRecord: reprezentuje kadencję kroków między odczytami.PowerRecord: reprezentuje moc wyjściową między odczytami, która jest powszechna w przypadku aktywności takich jak jazda na rowerze.
Pełną listę typów danych znajdziesz w artykule Typy danych Health Connect.
Trasy ćwiczeń
Za pomocą ExerciseRoute możesz powiązać trasę z treningami na świeżym powietrzu. Trasy składają się z sekwencyjnych obiektów ExerciseRoute.Location, z których każdy zawiera:
- długość i szerokość geograficzną,
- opcjonalną wysokość,
- opcjonalny kierunek,
- informacje o dokładności,
- sygnaturę czasową.
Łączenie tras sesji
ExerciseRoute zawiera sekwencyjne dane o lokalizacji sesji ćwiczeń. Nie jest traktowany jako niezależny rekord w Health Connect. Zamiast tego dane ExerciseRoute podajesz podczas wstawiania lub aktualizowania ExerciseSessionRecord.
Uwagi dotyczące tworzenia
Aplikacje do śledzenia treningów często muszą działać przez dłuższy czas, zwykle w tle, gdy ekran jest wyłączony. Podczas tworzenia funkcji treningowych ważne jest, aby zastanowić się, jak zarządzać wykonywaniem w tle i poprosić o niezbędne uprawnienia do danych treningowych.
Wykonywanie w tle
Aplikacje do treningów zwykle działają, gdy ekran jest wyłączony. W takim przypadku należy używać:
- usług na pierwszym planie do określania lokalizacji i próbkowania danych z czujników,
WorkManagerdo odroczonego zapisu lub synchronizacji,- strategii grupowania do regularnego zapisu rekordów.
Aby zachować ciągłość, używaj tego samego identyfikatora sesji we wszystkich zapisach.
Uprawnienia
Zanim zaczniesz odczytywać lub zapisywać dane treningowe, Twoja aplikacja musi poprosić o odpowiednie uprawnienia Health Connect. Typowe uprawnienia do treningów obejmują sesje ćwiczeń, trasy ćwiczeń i wskaźniki takie jak tętno czy prędkość. między innymi na następujące działania:
- Sesje ćwiczeń: uprawnienia do odczytu i zapisu
ExerciseSessionRecord. - Trasy ćwiczeń: uprawnienia do odczytu i zapisu
ExerciseRoute. - Tętno: uprawnienia do odczytu i zapisu
HeartRateRecord. - Prędkość: uprawnienia do odczytu i zapisu
SpeedRecord. - Dystans: uprawnienia do odczytu i zapisu
DistanceRecord. - Kalorie: uprawnienia do odczytu i zapisu
TotalCaloriesBurnedRecord. - Zdobyta wysokość: uprawnienia do odczytu i zapisu
ElevationGainedRecord. - Kadencja kroków: uprawnienia do odczytu i zapisu
StepsCadenceRecord. - Moc: uprawnienia do odczytu i zapisu
PowerRecord. - Kroki: uprawnienia do odczytu i zapisu
StepsRecord.
Poniżej znajdziesz przykład, jak poprosić o kilka uprawnień do sesji treningowej, która obejmuje dane o trasie, tętnie, dystansie, kaloriach, prędkości i krokach:
Po utworzeniu instancji klienta aplikacja musi poprosić użytkownika o uprawnienia. Użytkownicy muszą mieć możliwość przyznawania i odmawiania uprawnień w dowolnym momencie. Aby to zrobić, utwórz zestaw uprawnień dla wymaganych typów danych. Upewnij się, że uprawnienia w zestawie są najpierw zadeklarowane w manifeście Androida.
val permissions = setOf( HealthPermission.getReadPermission(ExerciseSessionRecord::class), HealthPermission.getWritePermission(ExerciseSessionRecord::class), HealthPermission.getReadPermission(HeartRateRecord::class), HealthPermission.getWritePermission(HeartRateRecord::class), HealthPermission.getReadPermission(SpeedRecord::class), HealthPermission.getWritePermission(SpeedRecord::class), HealthPermission.getReadPermission(DistanceRecord::class), HealthPermission.getWritePermission(DistanceRecord::class), HealthPermission.getReadPermission(TotalCaloriesBurnedRecord::class), HealthPermission.getWritePermission(TotalCaloriesBurnedRecord::class), HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class) )
getGrantedPermissions
, aby sprawdzić, czy Twoja aplikacja ma już przyznane wymagane uprawnienia. Jeśli nie, użyj
createRequestPermissionResultContract
aby poprosić o te uprawnienia. Spowoduje to wyświetlenie ekranu uprawnień Health Connect.
val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class), HealthPermission.getReadPermission(HeartRateRecord::class), HealthPermission.getWritePermission(HeartRateRecord::class) ) val requestPermissionsLauncher = rememberLauncherForActivityResult( contract = PermissionController.createRequestPermissionResultContract() ) { grantedPermissions -> if (grantedPermissions.containsAll(permissions)) { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") } } else { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") } } }
Aby poprosić o uprawnienia, wywołaj funkcję checkPermissionsAndRun:
if (!granted.containsAll(permissions)) { // Check if required permissions are not granted, and return return emptySet() } // Permissions already granted; proceed with inserting or reading data
Jeśli musisz poprosić o uprawnienia tylko do jednego typu danych, np. tętna, uwzględnij w zestawie uprawnień tylko ten typ danych:
Dostęp do tętna jest chroniony przez te uprawnienia:
android.permission.health.READ_HEART_RATEandroid.permission.health.WRITE_HEART_RATE
Aby dodać do aplikacji możliwość odczytywania tętna, zacznij od poproszenia o uprawnienia do typu danych HeartRateRecord.
Oto uprawnienie, które musisz zadeklarować, aby móc zapisywać dane o tętnie:
<application>
<uses-permission
android:name="android.permission.health.WRITE_HEART_RATE" />
...
</application>
Aby odczytać dane o tętnie, musisz poprosić o te uprawnienia:
<application>
<uses-permission
android:name="android.permission.health.READ_HEART_RATE" />
...
</application>
Implementowanie sesji treningowej
W tej sekcji opisujemy zalecany przepływ pracy podczas rejestrowania danych treningowych.
Rozpoczynanie sesji
Aby utworzyć nowy trening:
- Wygeneruj unikalny identyfikator sesji: sprawdź, czy ten identyfikator jest stabilny. Jeśli proces aplikacji zostanie zakończony i ponownie uruchomiony, musisz mieć możliwość wznowienia sesji za pomocą tego samego identyfikatora, aby zapobiec fragmentacji sesji.
- Ustaw
metadata.clientRecordId, aby zapobiec duplikatom podczas ponawiania synchronizacji. - Zapisz
ExerciseSessionRecord: uwzględnij czas rozpoczęcia. - Zacznij zbierać dane o typie danych i dane GPS: zacznij je zbierać dopiero po pomyślnym zainicjowaniu rekordu sesji.
Przykład:
val sessionClientId = UUID.randomUUID().toString() val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime) val session = ExerciseSessionRecord( startTime = startTime, startZoneOffset = zoneOffset, endTime = startTime.plusSeconds(3600), endZoneOffset = zoneOffset, exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING, metadata = Metadata(clientRecordId = sessionClientId), ) healthConnectClient.insertRecords(listOf(session))
Rejestrowanie tras ćwiczeń
Więcej informacji o odczytywaniu danych znajdziesz w artykule Odczytywanie surowych danych.
Podczas rejestrowania trasy ćwiczeń należy grupować dane. Oznacza to, że zamiast zapisywać każdy punkt GPS osobno, zbierasz grupę punktów i zapisujesz je wszystkie naraz w jednym wywołaniu.
Jest to ważne, ponieważ za każdym razem, gdy aplikacja odczytuje lub zapisuje dane w Health Connect, zużywa niewielką ilość baterii i mocy obliczeniowej.
Poniższy kod pokazuje, jak rejestrować dane w partiach:
// 1. Create a list to hold your route locations
val routeLocations = mutableListOf<ExerciseRoute.Location>()
// 2. Add points to your list as the exercise happens
routeLocations.add(
ExerciseRoute.Location(
time = Instant.now(),
latitude = 37.7749,
longitude = -122.4194
)
)
// ... keep adding points over a period of time ...
// 3. Save the whole list at once (Batching)
val session = ExerciseSessionRecord(
startTime = startTime,
endTime = endTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
// We pass the whole list here
exerciseRoute = ExerciseRoute(routeLocations)
)
healthConnectClient.insertRecords(listOf(session))
Zakończ sesję
Po zatrzymaniu zbierania danych:
- Zaktualizuj rekord: Twoja aplikacja aktualizuje
ExerciseSessionRecordza pomocąendTime. - Sfinalizuj dane: opcjonalnie oblicz wartości podsumowujące (np. łączny dystans lub średnie tempo) i zapisz je jako dodatkowe rekordy.
val finishedSession = session.copy(endTime = Instant.now())
healthConnectClient.updateRecords(listOf(finishedSession))
Odczytywanie danych treningowych
Aplikacje mogą odczytywać sesje ćwiczeń i powiązane z nimi dane, aby podsumowywać aktywność, dostarczać informacje o zdrowiu lub synchronizować dane z serwerem zewnętrznym. Możesz na przykład odczytać ExerciseSessionRecord, a następnie wysłać zapytanie o HeartRateRecord lub DistanceRecord, które wystąpiły w tym samym przedziale czasu.
Jeśli musisz zsynchronizować dane treningowe z serwerem backendowym lub aktualizować magazyn danych aplikacji za pomocą Health Connect, użyj dzienników zmian. Umożliwia to pobieranie listy wstawionych, zaktualizowanych lub usuniętych rekordów od określonego momentu, co jest bardziej wydajne niż ręczne śledzenie zmian lub wielokrotne odczytywanie wszystkich danych. Więcej informacji znajdziesz w artykule Synchronizowanie danych z Health Connect.
Odczytywanie sesji
Aby odczytać sesje ćwiczeń, użyj ReadRecordsRequest z ExerciseSessionRecord jako typem. Zwykle filtrujesz je według określonego zakresu czasu.
suspend fun readExerciseSessions(
healthConnectClient: HealthConnectClient,
startTime: Instant,
endTime: Instant
) {
val response = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = ExerciseSessionRecord::class,
timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
)
)
for (exerciseRecord in response.records) {
// Process each session
val exerciseType = exerciseRecord.exerciseType
val notes = exerciseRecord.notes
}
}
Odczytywanie tras
Chociaż dane ExerciseRoute są zapisywane jako część sesji ćwiczeń, należy je odczytywać oddzielnie. Aby odczytać dane trasy, użyj metody getExerciseRoute() z identyfikatorem sesji:
suspend fun readExerciseRoute(
healthConnectClient: HealthConnectClient,
exerciseSessionRecord: ExerciseSessionRecord
) {
// Check if the session has a route
val route = healthConnectClient.getExerciseRoute(
exerciseSessionRecordId = exerciseSessionRecord.metadata.id
)
when (route) {
is ExerciseRouteResponse.Success -> {
val locations = route.exerciseRoute.locations
for (location in locations) {
// Use latitude, longitude, and altitude
}
}
is ExerciseRouteResponse.NoData -> {
// Handle case where no route exists
}
is ExerciseRouteResponse.ConsentRequired -> {
// Handle case where permissions are missing
}
}
}
Odczytywanie typów danych
Aby odczytać szczegółowe dane (np. tętno), które wystąpiły podczas sesji, użyj startTime i endTime sesji, aby odfiltrować żądanie dla tego typu danych.
suspend fun readHeartRateData(
healthConnectClient: HealthConnectClient,
exerciseSession: ExerciseSessionRecord
) {
val response = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = HeartRateRecord::class,
timeRangeFilter = TimeRangeFilter.between(
exerciseSession.startTime,
exerciseSession.endTime
)
)
)
for (heartRateRecord in response.records) {
for (sample in heartRateRecord.samples) {
val bpm = sample.beatsPerMinute
}
}
}
Sprawdzone metody
Aby zwiększyć niezawodność danych i wygodę użytkowników, postępuj zgodnie z tymi wytycznymi:
- Często zapisuj dane podczas aktywnego śledzenia: w przypadku aktywnego śledzenia zapisuj dane, gdy staną się dostępne, lub w maksymalnym odstępie 15 minut.
- Do synchronizacji w tle używaj WorkManager: do odroczonych zapisów używaj
WorkManager. Aby zachować równowagę między danymi w czasie rzeczywistym a wydajnością baterii, używaj odstępu 15 minut. - Grupuj żądania zapisu: nie zapisuj każdego zdarzenia z czujnika osobno. Dziel żądania na części. Health Connect obsługuje do 1000 rekordów na żądanie zapisu.
- Używaj stabilnych i unikalnych identyfikatorów sesji: używaj spójnych identyfikatorów sesji. Jeśli sesja jest edytowana lub aktualizowana, użycie tego samego identyfikatora zapobiega traktowaniu jej jako nowej, oddzielnej sesji.
- Grupuj zarówno typy danych, jak i punkty trasy: aby zmniejszyć obciążenie wejścia/wyjścia i oszczędzać baterię, grupuj punkty danych w jednym wywołaniu
insertRecordszamiast zapisywać każdy punkt osobno. - Unikaj zapisywania zduplikowanych danych: używaj identyfikatorów klienta: podczas tworzenia rekordów ustaw
metadata.clientRecordId. Health Connect używa go do identyfikowania unikalnych rekordów. Jeśli spróbujesz zapisać rekord zclientRecordId, który już istnieje, Health Connect zignoruje duplikat lub zaktualizuje istniejący rekord zamiast tworzyć nowy. Ustawieniemetadata.clientRecordIdto najskuteczniejszy sposób na zapobieganie duplikatom podczas ponawiania synchronizacji lub ponownej instalacji aplikacji.val record = StepsRecord( count = 100, startTime = startTime, endTime = endTime, startZoneOffset = ZoneOffset.UTC, endZoneOffset = ZoneOffset.UTC, metadata = Metadata( // Use a unique ID from your own database clientRecordId = "daily_steps_2023_10_27_user_123" ) )
- Sprawdź istniejące dane: przed synchronizacją wysyłaj zapytania o zakres czasu, aby sprawdzić, czy rekordy z Twojej aplikacji już istnieją.
- Sprawdź dokładność GPS-a: przed zapisaniem w
ExerciseRouteodfiltruj próbki GPS o niskiej dokładności (np. punkty o dużym promieniu dokładności poziomej), aby sprawdzić, czy mapa wygląda czysto i profesjonalnie. - Upewnij się, że sygnatury czasowe się nie nakładają: sprawdź, czy nowa sesja nie rozpoczyna się przed zakończeniem poprzedniej. Nakładające się sesje mogą powodować konflikty na panelach fitness i w obliczeniach podsumowujących.
- Podaj jasne uzasadnienie uprawnień: użyj przepływu
Permission.createIntent, aby wyjaśnić, dlaczego Twoja aplikacja potrzebuje dostępu do danych dotyczących zdrowia, np. „Aby monitorować trendy ciśnienia krwi i dostarczać statystyki”. - Obsługuj wstrzymywanie i wznawianie: sprawdź, czy Twoja aplikacja prawidłowo obsługuje wstrzymywanie. Gdy użytkownik wstrzyma sesję, zatrzymaj zbieranie punktów trasy i typów danych, aby średnie tempo i czas trwania pozostały dokładne.
- Testuj długotrwałe sesje: monitoruj zużycie baterii podczas sesji trwających kilka godzin, aby sprawdzić, czy interwał grupowania i użycie czujników nie wyczerpują baterii urządzenia.
- Dopasuj sygnatury czasowe do częstotliwości czujników: aby zachować wysoką jakość danych, dopasuj sygnatury czasowe rekordów do rzeczywistej częstotliwości czujników.
Testowanie
Aby sprawdzić poprawność danych i wysoką jakość obsługi, postępuj zgodnie z tymi strategiami testowania i zapoznaj się z oficjalną dokumentacją Testowanie najczęstszych przypadków użycia.
Narzędzia do weryfikacji
- Health Connect Toolbox: użyj tej aplikacji towarzyszącej, aby ręcznie sprawdzać rekordy, usuwać dane testowe i symulować zmiany w bazie danych. To najlepszy sposób na sprawdzenie, czy rekordy są prawidłowo przechowywane.
- Testowanie jednostkowe za pomocą
FakeHealthConnectClient: użyj biblioteki testowej aby sprawdzić, jak Twoja aplikacja obsługuje przypadki brzegowe, takie jak cofnięcie uprawnień lub wyjątki API bez konieczności używania urządzenia fizycznego.
Lista kontrolna jakości
Typowa architektura
Implementacja treningu zwykle obejmuje:
| Komponent | Zarządza |
|---|---|
| Kontroler sesji | Stan sesji Timer Logika grupowania Kontrolery typów danych Próbkowanie lokalizacji |
| Warstwa repozytorium (zawiera operacje Health Connect): | Wstawianie sesji Wstawianie typów danych Wstawianie punktów trasy Odczytywanie podsumowań sesji |
| Warstwa interfejsu (wyświetla): | Czas trwania Typy danych na żywo Podgląd mapy Obliczenia podziału Ślad GPS na żywo |
Rozwiązywanie problemów
| Krótki opis problemu | Możliwa przyczyna | Rozdzielczość |
|---|---|---|
| Trasa nie jest powiązana z sesją | Niezgodność identyfikatora sesji lub zakresu czasu. | Sprawdź, czy ExerciseRoute jest zapisywana z zakresem czasu, który w całości mieści się w czasie trwania ExerciseSessionRecord. Jeśli później odwołujesz się do sesji, sprawdź, czy używasz spójnych identyfikatorów. Zobacz Rejestrowanie tras ćwiczeń. |
| Brakujące typy danych (np. tętno) | Brak uprawnień do zapisu lub nieprawidłowe filtry czasu. | Sprawdź, czy użytkownik poprosił o uprawnienia do określonego typu danych i czy je przyznał. Sprawdź, czy ReadRecordsRequest używa TimeRangeFilter, który pasuje do sesji. Zobacz Uprawnienia. |
| Nie udało się zapisać sesji | Nakładające się sygnatury czasowe. | Health Connect może odrzucać rekordy, które nakładają się na istniejące dane z tej samej aplikacji. Sprawdź, czy startTime nowej sesji przypada po endTime poprzedniej. |
| Nie zarejestrowano danych GPS | Usługa na pierwszym planie została zakończona lub jest nieaktywna. | Aby zbierać dane, gdy ekran jest wyłączony, musisz używać usługi na pierwszym planie z atrybutem foregroundServiceType="health" lub lokalizacji. |
| Wyświetlają się zduplikowane rekordy | Brak clientRecordId. |
Przypisz unikalny clientRecordId w Metadata każdego rekordu. Umożliwia to Health Connect usuwanie duplikatów, jeśli te same dane zostaną zapisane 2 razy podczas ponawiania synchronizacji. Zobacz Sprawdzone metody. |
Typowe czynności debugowania
| Sprawdź stan uprawnień. | Zanim spróbujesz wykonać operację odczytu lub zapisu, zawsze wywołaj getPermissionStatus(). Użytkownicy mogą w dowolnym momencie cofnąć uprawnienia w ustawieniach systemu. |
| Sprawdź tryb wykonywania. | Jeśli Twoja aplikacja nie zbiera danych w tle, sprawdź, czy masz zadeklarowane prawidłowe uprawnienia w pliku AndroidManifest.xml i czy użytkownik nie ustawił aplikacji w trybie „Ograniczenie baterii”. |