Rejestrowanie ćwiczenia w Ćwiczeniem klienta

Usługi zdrowotne zapewniają najwyższej klasy wsparcie dla aplikacji do ćwiczeń za pomocą interfejsu ExerciseClient. Dzięki ExerciseClient aplikacja może kontrolować czas trwania ćwiczeń, dodawać cele związane z ćwiczeniami i otrzymywać aktualne informacje o stanie ćwiczenia, zdarzeniach związanych z ćwiczeniami i innych istotnych danych. Aby dowiedzieć się więcej, zapoznaj się z pełną listą typów ćwiczeń obsługiwanych przez usługi zdrowotne.

Zobacz przykładowe ćwiczenie na GitHubie.

Dodaj zależności

Aby dodać zależność od usług zdrowotnych, musisz dodać do projektu repozytorium Google Maven. Więcej informacji znajdziesz w repozytorium Google Maven.

Następnie w pliku build.gradle na poziomie modułu dodaj tę zależność:

Odlotowy

dependencies {
    implementation "androidx.health:health-services-client:1.1.0-alpha02"
}

Kotlin

dependencies {
    implementation("androidx.health:health-services-client:1.1.0-alpha02")
}

Struktura aplikacji

Tworząc aplikację do ćwiczeń w Usługach medycznych, użyj tej struktury:

Kiedy przygotowujesz się do treningu i w trakcie treningu, Twoja aktywność może zostać zatrzymana z różnych powodów. Użytkownik może przełączyć się na inną aplikację lub wrócić do tarczy zegarka. System może wyświetlić coś u góry informacji lub wyłączyć ekran po okresie braku aktywności. Używaj stale uruchomionej funkcji ForegroundService w połączeniu z parametrem ExerciseClient, aby zapewnić prawidłowe działanie całego treningu.

ForegroundService pozwala użyć interfejsu Ongoing Activity API, aby wyświetlić wskaźnik na tarczy zegarka, co pozwoli użytkownikowi szybko wrócić do treningu.

Bardzo ważne jest prawidłowe żądanie danych o lokalizacji w usłudze na pierwszym planie. W pliku manifestu wskaż foregroundServiceType="location" i określ odpowiednie uprawnienia.

Używaj danych AmbientLifecycleObserver w przypadku aktywności przed treningiem, która zawiera wywołanie prepareExercise() i aktywność treningową. Nie aktualizuj jednak wyświetlacza w trakcie treningu, gdy działa on w trybie nieaktywnym. Dzieje się tak, ponieważ usługi zdrowotne zbierają dane treningowe, gdy ekran urządzenia jest w trybie nieaktywnym, aby oszczędzać energię, więc wyświetlane informacje mogą być nieaktualne. Podczas treningu pokazuj dane istotne dla użytkownika, takie jak aktualne informacje lub pusty ekran.

Sprawdź możliwości

Każdy ExerciseType obsługuje określone typy danych do celów związanych z ćwiczeniami i wskaźników. Sprawdź te możliwości na początku, bo mogą się one różnić w zależności od urządzenia. Urządzenie może nie obsługiwać określonego rodzaju ćwiczeń lub konkretnej funkcji, takiej jak automatyczne wstrzymywanie. Poza tym możliwości urządzenia mogą się zmieniać z czasem, np. po aktualizacji oprogramowania.

Po uruchomieniu aplikacji wyślij zapytanie o możliwości urządzenia oraz zapisz i przetwarzaj te elementy:

  • Ćwiczenia obsługiwane przez platformę.
  • Funkcje obsługiwane w przypadku każdego ćwiczenia.
  • Typy danych obsługiwane w przypadku każdego ćwiczenia.
  • Uprawnienia wymagane w przypadku każdego z tych typów danych.

Użyj operatora ExerciseCapabilities.getExerciseTypeCapabilities() z wybranym typem ćwiczenia, aby zobaczyć, o jakie wskaźniki możesz prosić, jakie cele możesz skonfigurować i jakie inne funkcje są dostępne w przypadku tego typu. Widać to w tym przykładzie:

val healthClient = HealthServices.getClient(this /*context*/)
val exerciseClient = healthClient.exerciseClient
lifecycleScope.launch {
    val capabilities = exerciseClient.getCapabilitiesAsync().await()
    if (ExerciseType.RUNNING in capabilities.supportedExerciseTypes) {
        runningCapabilities =
            capabilities.getExerciseTypeCapabilities(ExerciseType.RUNNING)
    }
}

W zwracanym obiekcie ExerciseTypeCapabilities supportedDataTypes zawiera listę typów danych, których możesz użyć. Różni się to w zależności od urządzenia, dlatego nie wysyłaj prośby o DataType, ponieważ może ona zakończyć się niepowodzeniem.

Użyj pól supportedGoals i supportedMilestones, aby określić, czy ćwiczenie wspomaga osiągnięcie wyznaczonego przez Ciebie celu.

Jeśli aplikacja umożliwia użytkownikowi korzystanie z automatycznego wstrzymywania, musisz sprawdzić, czy urządzenie obsługuje tę funkcję za pomocą supportsAutoPauseAndResume. ExerciseClient odrzuca żądania, które nie są obsługiwane na urządzeniu.

W tym przykładzie sprawdzisz obsługę typu danych HEART_RATE_BPM, możliwości celu STEPS_TOTAL i automatycznego wstrzymywania:

// Whether we can request heart rate metrics.
supportsHeartRate = DataType.HEART_RATE_BPM in runningCapabilities.supportedDataTypes

// Whether we can make a one-time goal for aggregate steps.
val stepGoals = runningCapabilities.supportedGoals[DataType.STEPS_TOTAL]
supportsStepGoals = 
    (stepGoals != null && ComparisonType.GREATER_THAN_OR_EQUAL in stepGoals)

// Whether auto-pause is supported.
val supportsAutoPause = runningCapabilities.supportsAutoPauseAndResume

Zarejestruj się, aby otrzymywać aktualizacje stanu ćwiczeń

Aktualizacje ćwiczeń są dostarczane do słuchacza. Aplikacja może zarejestrować tylko 1 detektor w danym momencie. Przed rozpoczęciem treningu skonfiguruj słuchawkę w sposób podany w poniższym przykładzie. Słuchacz otrzymuje tylko powiadomienia o ćwiczeniach, które posiada Twoja aplikacja.

val callback = object : ExerciseUpdateCallback {
    override fun onExerciseUpdateReceived(update: ExerciseUpdate) {
        val exerciseStateInfo = update.exerciseStateInfo
        val activeDuration = update.activeDurationCheckpoint
        val latestMetrics = update.latestMetrics
        val latestGoals = update.latestAchievedGoals
    }

    override fun onLapSummaryReceived(lapSummary: ExerciseLapSummary) {
        // For ExerciseTypes that support laps, this is called when a lap is marked.
    }

    override fun onAvailabilityChanged(
        dataType: DataType<*, *>,
        availability: Availability
    ) {
        // Called when the availability of a particular DataType changes.
        when {
            availability is LocationAvailability -> // Relates to Location/GPS.
            availability is DataTypeAvailability -> // Relates to another DataType.
        }
    }
}
exerciseClient.setUpdateCallback(callback)

Zarządzaj czasem trwania ćwiczeń

Usługi zdrowotne obsługują maksymalnie jedno ćwiczenie jednocześnie we wszystkich aplikacjach na urządzeniu. Jeśli ćwiczenie jest śledzone, a inna aplikacja zacznie śledzić nowe ćwiczenie, pierwsze ćwiczenie zostanie zakończone.

Przed rozpoczęciem ćwiczenia wykonaj następujące czynności:

  • Sprawdź, czy ćwiczenie jest już śledzone, i zareaguj odpowiednio. Na przykład poproś użytkownika o potwierdzenie, zanim zastąpisz poprzednie ćwiczenie i zaczniesz śledzić nowe.

Ten przykład pokazuje, jak sprawdzić istniejące ćwiczenie za pomocą getCurrentExerciseInfoAsync:

lifecycleScope.launch {
    val exerciseInfo = exerciseClient.getCurrentExerciseInfoAsync().await()
    when (exerciseInfo.exerciseTrackedStatus) {
        OTHER_APP_IN_PROGRESS -> // Warn user before continuing, will stop the existing workout.
        OWNED_EXERCISE_IN_PROGRESS -> // This app has an existing workout.
        NO_EXERCISE_IN_PROGRESS -> // Start a fresh workout.
    }
}

Uprawnienia

Gdy używasz funkcji ExerciseClient, sprawdź, czy aplikacja żąda i utrzymuje niezbędne uprawnienia. Jeśli aplikacja używa danych LOCATION, upewnij się, że aplikacja prosi o dostęp do nich i utrzymuje do nich odpowiednie uprawnienia.

W przypadku wszystkich typów danych przed wywołaniem funkcji prepareExercise() lub startExercise() wykonaj te czynności:

  • W pliku AndroidManifest.xml określ odpowiednie uprawnienia dla żądanych typów danych.
  • Sprawdź, czy użytkownik przyznał niezbędne uprawnienia. Więcej informacji znajdziesz w artykule o wysyłaniu prośby o uprawnienia aplikacji. Usługi zdrowotne odrzucają żądanie, jeśli nie przyznano jeszcze niezbędnych uprawnień.

W przypadku danych o lokalizacji wykonaj te dodatkowe czynności:

Przygotuj się do treningu

Niektóre czujniki, takie jak GPS czy tętno, mogą potrzebować chwili na rozgrzewkę, a użytkownik może chcieć sprawdzić swoje dane przed rozpoczęciem treningu. Opcjonalna metoda prepareExerciseAsync() pozwala czujnikom nagrzewać się i odbierać dane bez uruchamiania stopera przed treningiem. Ten czas przygotowania nie ma wpływu na activeDuration.

Zanim nawiążesz połączenie z numerem prepareExerciseAsync(), sprawdź te elementy:

  • Sprawdź ustawienie lokalizacji na całej platformie. Użytkownik kontroluje to ustawienie w głównym menu Ustawienia. Różni się ono od sprawdzania uprawnień na poziomie aplikacji.

    Jeśli to ustawienie jest wyłączone, powiadom użytkownika, że odmówił dostępu do lokalizacji, i poproś o jego włączenie, jeśli aplikacja wymaga dostępu do lokalizacji.

  • Sprawdź, czy aplikacja ma uprawnienia w czasie działania dotyczące czujników na ciele, rozpoznawania aktywności i dokładnej lokalizacji. W przypadku brakujących uprawnień poproś użytkownika o przyznanie uprawnień czasu działania i podaj odpowiedni kontekst. Jeśli użytkownik nie przyzna określonych uprawnień, usuń typy danych powiązane z tym uprawnieniem z wywołania funkcji prepareExerciseAsync(). Jeśli nie przyznano dostępu do czujnika na ciele ani do lokalizacji, nie wywołuj prepareExerciseAsync(), ponieważ rozmowa ta ma na celu uzyskanie stabilnego tętna lub pozycji GPS przed rozpoczęciem ćwiczenia. Aplikacja nadal może pobierać za nią dystans, tempo, prędkość i inne dane, które nie wymagają tych uprawnień.

Aby zapewnić prawidłowe wywołanie metody prepareExerciseAsync(), wykonaj te czynności:

  • Użyj AmbientLifecycleObserver w przypadku aktywności przed treningiem, która obejmuje przygotowanie rozmowy.
  • Wywołaj prepareExerciseAsync() z usługi na pierwszym planie. Jeśli nie jest on objęty usługą i jest powiązany z cyklem życia aktywności, przygotowywanie czujnika może zostać niepotrzebnie przerwane.
  • Wywołaj polecenie endExercise(), aby wyłączyć czujniki i zmniejszyć zużycie energii, gdy użytkownik opuści aktywność przed treningiem.

Ten przykład pokazuje, jak wywołać prepareExerciseAsync():

val warmUpConfig = WarmUpConfig(
    ExerciseType.RUNNING,
    setOf(
        DataType.HEART_RATE_BPM,
        DataType.LOCATION
    )
)
// Only necessary to call prepareExerciseAsync if body sensor or location
//permissions are given
exerciseClient.prepareExerciseAsync(warmUpConfig).await()

// Data and availability updates are delivered to the registered listener.

Gdy aplikacja ma stan PREPARING, aktualizacje informacji o dostępności czujników są dostarczane w klastrach ExerciseUpdateCallback do onAvailabilityChanged(). Informacje te można następnie wyświetlić użytkownikowi, aby mógł zdecydować, czy rozpocząć trening.

Rozpocznij trening

Gdy chcesz rozpocząć ćwiczenie, utwórz ExerciseConfig, aby skonfigurować typ ćwiczenia, typy danych, o których chcesz otrzymywać wskaźniki, oraz cele związane z ćwiczeniami i etapami milowymi.

Cele ćwiczeń składają się z DataType i warunku. Cele związane z ćwiczeniami to cel jednorazowy, który jest aktywowany, gdy zostanie spełniony warunek, np. gdy użytkownik przebiegnie określony dystans. Możesz też określić kamień milowy dotyczący ćwiczenia. Kamienie milowe ćwiczenia mogą być aktywowane wiele razy, na przykład za każdym razem, gdy użytkownik przebiegnie określony punkt po ustalonym dystansie.

Z przykładu poniżej dowiesz się, jak utworzyć jeden cel każdego typu:

const val CALORIES_THRESHOLD = 250.0
const val DISTANCE_THRESHOLD = 1_000.0 // meters

suspend fun startExercise() {
    // Types for which we want to receive metrics.
    val dataTypes = setOf(
        DataType.HEART_RATE_BPM,
        DataType.CALORIES_TOTAL,
        DataType.DISTANCE
    )

    // Create a one-time goal.
    val calorieGoal = ExerciseGoal.createOneTimeGoal(
        DataTypeCondition(
            dataType = DataType.CALORIES_TOTAL,
            threshold = CALORIES_THRESHOLD,
            comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
        )
    )

    // Create a milestone goal. To make a milestone for every kilometer, set the initial
    // threshold to 1km and the period to 1km.
    val distanceGoal = ExerciseGoal.createMilestone(
        condition = DataTypeCondition(
            dataType = DataType.DISTANCE_TOTAL,
            threshold = DISTANCE_THRESHOLD,
            comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
        ),
        period = DISTANCE_THRESHOLD
    )

    val config = ExerciseConfig(
        exerciseType = ExerciseType.RUNNING,
        dataTypes = dataTypes,
        isAutoPauseAndResumeEnabled = false,
        isGpsEnabled = true,
        exerciseGoals = mutableListOf<ExerciseGoal<Double>>(calorieGoal, distanceGoal)
    )
    exerciseClient.startExerciseAsync(config).await()
}

Możesz też oznaczyć etapy dla wszystkich ćwiczeń. Usługi zdrowotne udostępniają ExerciseLapSummary ze wskaźnikami zbiorczymi z całego okresu.

Poprzedni przykład przedstawia użycie właściwości isGpsEnabled, które musi być spełnione podczas żądania danych o lokalizacji. Korzystanie z GPS-u może być też pomocne w przypadku innych danych. Jeśli ExerciseConfig określa odległość w polu DataType, domyślną wartością jest podanie odległości do szacowania za pomocą kroków. Jeśli włączysz GPS, do oszacowania odległości możesz używać informacji o lokalizacji.

Zatrzymywanie, wznawianie i kończenie treningu

Możesz wstrzymywać, wznawiać i kończyć treningi, korzystając z odpowiedniej metody, np. pauseExerciseAsync() lub endExerciseAsync().

Użyj stanu z: ExerciseUpdate jako źródła informacji. Trening nie jest uznawany za wstrzymany, gdy wywołanie pauseExerciseAsync() powróci, tylko gdy zostanie on odzwierciedlony w komunikacie ExerciseUpdate. Jest to szczególnie ważne, jeśli chodzi o stany interfejsu. Jeśli użytkownik naciśnie przycisk pauzy, wyłącz przycisk pauzy i wywołaj polecenie pauseExerciseAsync() w usługach zdrowotnych. Poczekaj, aż Usługi zdrowotne osiągną stan wstrzymania za pomocą polecenia ExerciseUpdate.exerciseStateInfo.state, a następnie przełącz przycisk, aby wznowić. Dzieje się tak, ponieważ aktualizacje stanu usług zdrowotnych mogą trwać dłużej niż naciśnięcie przycisku. Jeśli więc powiążesz wszystkie zmiany w interfejsie z naciśnięciem przycisku, interfejs może nie być zsynchronizowany ze stanem usług zdrowotnych.

Pamiętaj o tym w tych sytuacjach:

  • Automatyczne wstrzymywanie jest włączone:trening może się zatrzymywać i rozpoczynać bez interakcji użytkownika.
  • Inna aplikacja rozpoczyna trening: trening może zostać zakończony bez interakcji użytkownika.

Jeśli trening w Twojej aplikacji zostanie zakończony przez inną aplikację, aplikacja musi płynnie poradzić sobie z zakończeniem:

  • Zapisz stan częściowego treningu, aby postępy użytkownika nie zostały wykasowane.
  • Usuń ikonę trwającej aktywności i wyślij użytkownikowi powiadomienie o zakończeniu treningu przez inną aplikację.

Weź też pod uwagę przypadki unieważniania uprawnień podczas nieustannego ćwiczenia. Jest ona wysyłana za pomocą stanu isEnded z ExerciseEndReason o wartości AUTO_END_PERMISSION_LOST. Postępuj tak samo jak w przypadku zamknięcia konta: zapisz stan częściowy, usuń ikonę trwającej aktywności i wyślij powiadomienie o tym, co się stało z użytkownikiem.

Z przykładu poniżej dowiesz się, jak sprawdzić, czy konto zostało zamknięte:

val callback = object : ExerciseUpdateCallback {
    override fun onExerciseUpdateReceived(update: ExerciseUpdate) {
        if (update.exerciseStateInfo.state.isEnded) {
            // Workout has either been ended by the user, or otherwise terminated
        }
        ...
    }
    ...
}

Zarządzaj aktywnym czasem trwania

Podczas ćwiczenia aplikacja może wyświetlać aktywny czas trwania treningu. Aplikacja, Usługi zdrowotne i Mikrokontroler Unity (MCU) (MCU) to procesor o małej mocy, który odpowiada za monitorowanie ćwiczeń. Muszą one być zsynchronizowane z takim samym bieżącym czasem trwania. Aby ułatwić zarządzanie tym procesem, Usługi zdrowotne wysyłają kod ActiveDurationCheckpoint udostępniający punkt zakotwiczenia, od którego aplikacja może uruchamiać licznik czasu.

Czas trwania aktywności jest wysyłany z MCU i może pojawić się w aplikacji dopiero po krótkim czasie, dlatego ActiveDurationCheckpoint zawiera 2 właściwości:

  • activeDuration: czas trwania ćwiczenia
  • time: kiedy obliczono aktywny czas trwania.

Dlatego w aplikacji czas trwania aktywności można obliczyć na podstawie wartości ActiveDurationCheckpoint za pomocą tego równania:

(now() - checkpoint.time) + checkpoint.activeDuration

Uwzględnia niewielką różnicę między aktywnym czasem trwania obliczonym w MCU a momentem pojawienia się w aplikacji. Można to wykorzystać do zapełnienia chronometru w aplikacji i zapewnić, że licznik czasu aplikacji będzie idealnie dopasowany do czasu w MCU.

Jeśli ćwiczenie jest wstrzymane, aplikacja czeka na ponowne uruchomienie stopera w interfejsie, aż obliczony czas upłynie tak długo, jak jest to aktualnie wyświetlane w interfejsie. Dzieje się tak, ponieważ sygnał wstrzymania dociera do służb zdrowotnych i MCU z niewielkim opóźnieniem. Na przykład, jeśli aplikacja zostanie wstrzymana po t=10 sekundach, usługi Health Services mogą nie dostarczyć do aplikacji aktualizacji PAUSED do t=10,2 sekundy.

Praca z danymi z Ćwiczenia klienta

Wskaźniki dotyczące typów danych, które zarejestrowała Twoja aplikacja, są dostarczane w wiadomościach ExerciseUpdate.

Procesor dostarcza wiadomości tylko po wybudzeniu lub po osiągnięciu maksymalnego okresu raportowania, np. co 150 sekund. Nie używaj częstotliwości ExerciseUpdate do przesuwania chronometru za pomocą activeDuration. Przykład implementacji niezależnego chronometru znajdziesz w przykładzie ćwiczenia na GitHubie.

Gdy użytkownik rozpoczyna trening, mogą być często dostarczane wiadomości z aplikacji ExerciseUpdate, np. co sekundę. Gdy użytkownik rozpoczyna trening, ekran może się wyłączyć. Dzięki temu usługi zdrowotne mogą dostarczać dane rzadziej, ale nadal próbkowane są z tą samą częstotliwością, aby uniknąć wybudzenia głównego procesora. Gdy użytkownik patrzy na ekran, wszelkie dane będące w trakcie grupowania są natychmiast dostarczane do aplikacji.

Kontroluj szybkość grupowania

W niektórych sytuacjach możesz chcieć kontrolować częstotliwość, z jaką aplikacja otrzymuje określone typy danych, gdy ekran jest wyłączony. Obiekt BatchingMode umożliwia aplikacji zastąpienie domyślnego działania wsadowego w celu częstszego dostarczania danych.

Aby skonfigurować szybkość grupowania, wykonaj te czynności:

  1. Sprawdź, czy urządzenie obsługuje konkretną definicję typu BatchingMode:

    // Confirm BatchingMode support to control heart rate stream to phone.
    suspend fun supportsHrWorkoutCompanionMode(): Boolean {
        val capabilities = exerciseClient.getCapabilities()
        return BatchingMode.HEART_RATE_5_SECONDS in
                capabilities.supportedBatchingModeOverrides
    }
    
  2. Określ, że obiekt ExerciseConfig powinien używać określonego BatchingMode, jak w tym fragmencie kodu.

    val config = ExerciseConfig(
        exerciseType = ExerciseType.WORKOUT,
        dataTypes = setOf(
            DataType.HEART_RATE_BPM,
            DataType.TOTAL_CALORIES
        ),
        // ...
        batchingModeOverrides = setOf(BatchingMode.HEART_RATE_5_SECONDS)
    )
    
  3. Opcjonalnie możesz dynamicznie konfigurować BatchingMode podczas treningu, aby nie utożsamiać się z konkretnym działaniem grupowania przez cały czas jego trwania:

    val desiredModes = setOf(BatchingMode.HEART_RATE_5_SECONDS)
    exerciseClient.overrideBatchingModesForActiveExercise(desiredModes)
    
  4. Aby wyczyścić dostosowane BatchingMode i przywrócić działanie domyślne, przekaż pusty zestaw do parametru exerciseClient.overrideBatchingModesForActiveExercise().

Sygnatury czasowe

Punkt w czasie każdego punktu danych reprezentuje czas od uruchomienia urządzenia. Aby przekonwertować wartość na sygnaturę czasową:

val bootInstant =
    Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())

Tej wartości możesz następnie używać w polu getStartInstant() lub getEndInstant() dla każdego punktu danych.

Dokładność danych

Niektóre typy danych mogą mieć informacje o dokładności związane z każdym punktem danych. Reprezentuje ją właściwość accuracy.

Klasy HrAccuracy i LocationAccuracy można wypełniać odpowiednio dla typów danych HEART_RATE_BPM i LOCATION. Jeśli jest dostępna, używaj właściwości accuracy do określania, czy każdy punkt danych ma wystarczająco dużą dokładność na potrzeby Twojej aplikacji.

Przechowywanie i przesyłanie danych

Użyj opcji Sala, aby przechowywać dane dostarczone z usług medycznych. Dane są przesyłane na koniec ćwiczenia za pomocą mechanizmu takiego jak Work Manager. Dzięki temu wywołania sieciowe do przesyłania danych są odraczane do zakończenia ćwiczenia, co minimalizuje zużycie energii w trakcie ćwiczenia i upraszcza pracę.

Lista kontrolna integracji

Zanim opublikujesz aplikację, która korzysta z elementu ExerciseClient w usługach zdrowotnych, zapoznaj się z tą listą kontrolną, aby uniknąć niektórych typowych problemów. Sprawdź, czy:

  • Przy każdym uruchomieniu aplikacja sprawdza możliwości danego rodzaju ćwiczenia i możliwości urządzenia. Dzięki temu możesz wykryć, czy dane urządzenie lub ćwiczenie nie obsługuje któregoś z typów danych, których potrzebuje Twoja aplikacja.
  • Żądasz niezbędnych uprawnień, utrzymujesz je i określasz w pliku manifestu. Przed wywołaniem funkcji prepareExerciseAsync() aplikacja potwierdza przyznanie uprawnień w czasie działania.
  • Aplikacja używa uprawnienia getCurrentExerciseInfoAsync() do obsługi przypadków, w których:
    • Ćwiczenie jest już śledzone, a aplikacja zastępuje poprzednie.
    • Inna aplikacja zakończyła ćwiczenie. Może się tak zdarzyć, gdy użytkownik ponownie uruchomi aplikację i zobaczy komunikat informujący o tym, że ćwiczenie zostało przerwane, ponieważ przejęła go inna aplikacja.
  • Jeśli używasz danych LOCATION:
    • Aplikacja zachowuje ForegroundService z odpowiednim foregroundServiceType przez cały czas trwania ćwiczenia (w tym rozmowy przygotowującej).
    • Sprawdź, czy na urządzeniu jest włączony GPS przy użyciu funkcji isProviderEnabled(LocationManager.GPS_PROVIDER). W razie potrzeby użytkownik poprosi o otworzenie ustawień lokalizacji.
    • W wymagających przypadkach, gdy otrzymywanie danych o lokalizacji z krótkim czasem oczekiwania jest bardzo ważne, rozważ zintegrowanie dostawcy uśrednionej lokalizacji i wykorzystanie jego danych jako początkowej poprawki lokalizacji. Jeśli dostępne są bardziej stabilne informacje o lokalizacji, użyj ich zamiast FLP.
  • Jeśli aplikacja wymaga przesłania danych, wszelkie wywołania sieciowe do przesyłania danych są odroczone do czasu zakończenia ćwiczenia. W przeciwnym razie podczas ćwiczenia aplikacja będzie oszczędnie wykonywać wszystkie niezbędne połączenia sieciowe.