Rejestrowanie ćwiczenia w Ćwiczeniem klienta

Usługi zdrowotne zapewniają najwyższej jakości wsparcie dla aplikacji treningowych za pomocą ExerciseClient. Dzięki ExerciseClient Twoja aplikacja może kontrolować, kiedy trwa ćwiczenie, dodawać cele ćwiczeń oraz otrzymywać informacje o stanie ćwiczeń, zdarzeniach ćwiczeń i innych danych. Więcej informacji znajdziesz na pełnej liście typów ćwiczeń obsługiwanych przez Health Services.

Zapoznaj się z przykładowym ćwiczeniem na GitHubie.

Dodawanie zależności

Aby dodać zależność od Health Services, musisz dodać do projektu repozytorium Google Maven. Więcej informacji znajdziesz w repozytorium Maven Google.

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

Groovy

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

Kotlin

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

Struktura aplikacji

Podczas tworzenia aplikacji do ćwiczeń za pomocą Health Services należy używać tej struktury aplikacji:

Podczas przygotowania się do treningu i w trakcie treningu Twoja aktywność może zostać przerwana 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świetlać coś na ekranie podczas Twojej aktywności lub wyłączyć ekran po okresie bezczynności. Używaj stale działającego ForegroundService w połączeniu z ExerciseClient, aby zapewnić prawidłowe działanie przez cały czas trwania treningu.

Dzięki ForegroundService możesz używać interfejsu API trwającej aktywności, aby wyświetlać na tarczach zegarka wskaźnik, który pozwala użytkownikowi szybko wrócić do treningu.

Ważne jest, aby w usłudze na pierwszym planie prawidłowo prosić o dane o lokalizacji. W pliku manifestu określ niezbędne typy usług na pierwszym planieuprawnienia:

<manifest ...>
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <application ...>
    
      <!-- If your app is designed only for devices that run Wear OS 4
           or lower, use android:foregroundServiceType="location" instead. -->
      <service
          android:name=".MyExerciseSessionRecorder"
          android:foregroundServiceType="health|location">
      </service>
      
    </application>
</manifest>

Użyj AmbientLifecycleObserver do aktywności przed treningiem, która zawiera wywołanie prepareExercise(), oraz do aktywności treningowej. Nie aktualizuj jednak wyświetlacza podczas treningu w trybie otoczenia: usługi zdrowotne grupowo zapisują dane treningowe, gdy ekran urządzenia jest w trybie otoczenia, aby oszczędzać energię. Wyświetlane informacje mogą być więc nieaktualne. Podczas treningów wyświetlaj dane, które mają sens dla użytkownika, pokazując aktualne informacje lub pusty ekran.

Sprawdzanie możliwości

Każda aplikacja ExerciseType obsługuje określone typy danych dla danych i celów ćwiczeń. Sprawdź te funkcje podczas uruchamiania, ponieważ mogą się one różnić w zależności od urządzenia. Urządzenie może nie obsługiwać określonego typu ćwiczeń lub określonej funkcji, np. automatycznej pauzy. Dodatkowo możliwości urządzenia mogą się zmieniać z czasem, np. po aktualizacji oprogramowania.

Podczas uruchamiania aplikacji zapytaj o możliwości urządzenia oraz przechowuj i przetwarzaj te dane:

  • Ćwiczenia, które obsługuje platforma.
  • Funkcje obsługiwane w przypadku każdego ćwiczenia.
  • Typy danych obsługiwane w przypadku poszczególnych ćwiczeń.
  • Uprawnienia wymagane w przypadku każdego z tych typów danych.

Użyj ExerciseCapabilities.getExerciseTypeCapabilities() z wybranym typem ćwiczeń, aby sprawdzić, jakie dane możesz pobrać, jakie cele ćwiczeń możesz skonfigurować i jakie inne funkcje są dostępne dla tego typu. Przykład:

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 elemencie ExerciseTypeCapabilities supportedDataTypes znajdują się typy danych, których dotyczy żądanie. Ta opcja różni się w zależności od urządzenia. Upewnij się, że nie prosisz o urządzenie DataType, które nie jest obsługiwane, ponieważ może to spowodować niepowodzenie prośby.

Użyj pól supportedGoals i supportedMilestones, aby sprawdzić, czy ćwiczenie może obsługiwać cel ćwiczenia, który chcesz utworzyć.

Jeśli Twoja aplikacja umożliwia użytkownikowi korzystanie z automatycznego wstrzymywania, musisz sprawdzić, czy ta funkcja jest obsługiwana przez urządzenie z supportsAutoPauseAndResume. ExerciseClient odrzuca żądania, które nie są obsługiwane na urządzeniu.

W tym przykładzie sprawdzamy obsługę typu danych HEART_RATE_BPM, możliwości celu STEPS_TOTAL i funkcji 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

Rejestracja w celu otrzymywania powiadomień o stanie ćwiczeń

Słuchacz otrzymuje powiadomienia o ćwiczeniach. Aplikacja może zarejestrować tylko jednego słuchacza naraz. Przed rozpoczęciem treningu skonfiguruj słuchacza, jak w poniższym przykładzie. Słuchacz otrzymuje tylko informacje o ćwiczeniach należących do aplikacji.

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ądzanie czasem trwania ćwiczenia

Usługi dotyczące zdrowia obsługują maksymalnie 1 ćwiczenie naraz we wszystkich aplikacjach na urządzeniu. Jeśli podczas śledzenia ćwiczenia inna aplikacja zacznie śledzić nowe ćwiczenie, pierwsze ćwiczenie zostanie zakończone.

Przed rozpoczęciem ćwiczenia:

  • Sprawdź, czy dane dotyczące ćwiczenia są już rejestrowane, i odpowiednio się zachowaj. Możesz na przykład poprosić użytkownika o potwierdzenie, zanim zastąpisz poprzednie ćwiczenie i zaczniesz rejestrować nowe.

Z tego przykładu dowiesz się, jak sprawdzić, czy istnieje już ćwiczenie o nazwie 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

Podczas korzystania z funkcji ExerciseClient upewnij się, że aplikacja prosi o wymagane uprawnienia i utrzymuje je. Jeśli Twoja aplikacja korzysta z danych LOCATION, upewnij się, że prosi o odpowiednie uprawnienia i je zachowuje.

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 ma niezbędne uprawnienia. Więcej informacji znajdziesz w artykule Prośba o uprawnienia aplikacji. Usługi zdrowotne odrzucają żądanie, jeśli wymagane uprawnienia nie zostały jeszcze przyznane.

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

Przygotowanie do treningu

Niektóre czujniki, takie jak GPS czy tętno, mogą potrzebować chwili na rozgrzanie się. Użytkownik może też chcieć zobaczyć swoje dane przed rozpoczęciem treningu. Opcjonalna metoda prepareExerciseAsync() pozwala tym czujnikom się rozgrzać, a dane są otrzymywane bez uruchamiania minutnika treningu. Czas przygotowania nie ma wpływu na activeDuration.

Zanim zadzwonisz do prepareExerciseAsync(), sprawdź te kwestie:

  • Sprawdź ustawienia lokalizacji na całej platformie. Użytkownik może kontrolować to ustawienie w głównym menu ustawień. Jest ono inne niż sprawdzanie uprawnień na poziomie aplikacji.

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

  • Sprawdź, czy Twoja aplikacja ma uprawnienia do odczytu danych z czujników na ciele (poziom API 35 lub niższy) lub tętna (poziom API 36 lub wyższy), a także czy umożliwia rozpoznawanie aktywności i dostęp do lokalizacji z dokładnością do metra. W przypadku braku uprawnień poproś użytkownika o przyznanie uprawnień w czasie działania, podając odpowiedni kontekst. Jeśli użytkownik nie udzieli określonego uprawnienia, usuń typy danych powiązane z tym uprawnieniem z wywołania do prepareExerciseAsync(). Jeśli nie masz uprawnień do czujnika ciała (częstotliwości pracy serca na poziomie interfejsu API 36 lub nowszego) ani uprawnień do lokalizacji, nie wywołuj metody prepareExerciseAsync(), ponieważ metoda prepare jest przeznaczona do uzyskiwania stabilnej częstotliwości pracy serca lub pozycji GPS przed rozpoczęciem ćwiczenia. Aplikacja może nadal uzyskiwać dane dotyczące odległości, tempa, prędkości i innych danych, które nie wymagają tych uprawnień.

Aby wywołanie funkcji prepareExerciseAsync() zakończyło się powodzeniem:

  • Użyj wartości AmbientLifecycleObserver do aktywności przed treningiem, która zawiera wywołanie prepare.
  • Zadzwoń do prepareExerciseAsync() z poziomu usługi na pierwszym planie. Jeśli nie jest ona w usłudze i jest powiązana z cyklem aktywności, przygotowanie czujnika może zostać niepotrzebnie przerwane.
  • Wywołaj funkcję endExercise(), aby wyłączyć czujniki i ograniczyć zużycie energii, jeśli użytkownik przejdzie do innej aktywności niż przedtreningowej.

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

val warmUpConfig = WarmUpConfig(
    ExerciseType.RUNNING,
    setOf(
        DataType.HEART_RATE_BPM,
        DataType.LOCATION
    )
)
// Only necessary to call prepareExerciseAsync if body sensor (API level 35
// or lower), heart rate (API level 36+), or location permissions are given.
exerciseClient.prepareExerciseAsync(warmUpConfig).await()

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

Gdy aplikacja jest w stanie PREPARING, aktualizacje dotyczące dostępności czujnika są dostarczane w ramach ExerciseUpdateCallback przez onAvailabilityChanged(). Te informacje mogą zostać wyświetlone użytkownikowi, aby mógł zdecydować, czy chce rozpocząć trening.

Rozpocznij trening

Aby rozpocząć ćwiczenie, utwórz ExerciseConfig, aby skonfigurować typ ćwiczenia, typy danych, dla których chcesz otrzymywać dane, oraz cele lub kamienie milowe ćwiczenia.

Cele ćwiczeń składają się z DataType i warunku. Cele ćwiczeń to cele jednorazowe, które są uruchamiane po spełnieniu określonego warunku, np. gdy użytkownik przebiegnie pewną odległość. Możesz też ustawić cel związany z ćwiczeniami. Osiągnięcia dotyczące ćwiczeń mogą być uruchamiane wielokrotnie, na przykład za każdym razem, gdy użytkownik przebiegnie pewną odległość w ramach określonego dystansu.

W tym przykładzie pokazujemy, jak utworzyć po jednym celu z 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ż oznaczać okrążenia w przypadku wszystkich ćwiczeń. Usługi zdrowotne udostępniają ExerciseLapSummary z danymi zagregowanymi w okresie okrążenia.

Poprzedni przykład pokazuje użycie wartości isGpsEnabled, która musi być zwrócona jako true, gdy żądasz danych o lokalizacji. Jednak korzystanie z GPS może też ułatwiać uzyskiwanie innych danych. Jeśli ExerciseConfig określa odległość jako DataType, domyślnie do oszacowania odległości używane są kroki. Opcjonalnie można włączyć GPS, aby informacje o lokalizacji były używane do szacowania odległości.

Wstrzymywanie, wznawianie i kończenie treningu

Ćwiczenia możesz wstrzymywać, wznawiać i kończyć, używając odpowiedniej metody, np. pauseExerciseAsync() lub endExerciseAsync().

Użyj stanu z ExerciseUpdate jako źródła informacji. Trening nie jest uważany za wstrzymany, gdy wywołanie funkcji pauseExerciseAsync() zwraca wartość, ale gdy stan jest odzwierciedlony w wiadomości ExerciseUpdate. Jest to szczególnie ważne w przypadku stanów interfejsu użytkownika. Jeśli użytkownik naciśnie przycisk pauzy, wyłącz przycisk pauzy i wywołaj funkcję pauseExerciseAsync() w Health Services. Zaczekaj, aż usługi zdrowotne przejdą do stanu wstrzymania za pomocą przycisku ExerciseUpdate.exerciseStateInfo.state, a następnie przełącz go, aby wznowić działanie. Dzieje się tak, ponieważ aktualizacje stanu usług zdrowotnych mogą być przesyłane dłużej niż naciśnięcie przycisku. Jeśli więc wszystkie zmiany w interfejsie będą powiązane z naciśnięciami przycisków, interfejs może stracić synchronizację ze stanem usług zdrowotnych.

Pamiętaj o tym w następujących sytuacjach:

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

Jeśli trening w aplikacji zostanie przerwany przez inną aplikację, aplikacja musi prawidłowo obsłużyć przerwanie:

  • Zapisywanie częściowego stanu treningu, aby nie tracić postępów użytkownika.
  • Usuń ikonę bieżącej aktywności i wyślij użytkownikowi powiadomienie z informacją, że jego trening został zakończony przez inną aplikację.

Zadbaj też o to, aby aplikacja mogła działać, gdy uprawnienia zostaną cofnięte w trakcie ćwiczenia. Jest on wysyłany w stanie isEnded z wartością ExerciseEndReasonAUTO_END_PERMISSION_LOST. W takim przypadku postępuj podobnie jak w przypadku rozwiązania umowy: zapisz stan częściowy, usuń ikonę bieżącej aktywności i wyślij powiadomienie o tym, co się stało użytkownikowi.

Ten przykład pokazuje, jak prawidłowo sprawdzić, czy subskrypcja została zakończona:

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ądzanie czasem trwania aktywności

Podczas ćwiczeń aplikacja może wyświetlać aktywny czas trwania treningu. Aplikacja, usługi zdrowotne i mikrokontroler (MCU) urządzenia (niskoprądowy procesor odpowiedzialny za śledzenie ćwiczeń) muszą być zsynchronizowane i mieć ten sam bieżący czas aktywności. Aby ułatwić zarządzanie tymi funkcjami, usługi zdrowotne wysyłają zdarzenie ActiveDurationCheckpoint, które stanowi punkt odniesienia, od którego aplikacja może uruchomić swój minutnik.

Czas trwania aktywnego połączenia jest wysyłany z MCU i może trochę potrwać, zanim dotrze do aplikacji. Dlatego ActiveDurationCheckpoint zawiera 2 właściwości:

  • activeDuration: czas trwania ćwiczenia,
  • time: czas trwania aktywnego okresu, w którym obliczono dane

Dlatego w aplikacji aktywny czas trwania ćwiczenia można obliczyć z poziomu ActiveDurationCheckpoint za pomocą tego równania:

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

Jest to spowodowane niewielką różnicą między czasem aktywnego działania obliczanym na MCU a czasem dotarcia do aplikacji. Można go wykorzystać do ustawienia zegara w aplikacji i upewnienia się, że zegar w aplikacji jest idealnie zsynchronizowany z czasem w Health Services i MCU.

Jeśli ćwiczenie jest wstrzymane, aplikacja czeka na ponowne uruchomienie minutnika w interfejsie, aż obliczony czas przekroczy czas wyświetlany obecnie w interfejsie. Dzieje się tak, ponieważ sygnał pauzy dociera do usług zdrowotnych i MCU z niewielkim opóźnieniem. Jeśli na przykład aplikacja jest wstrzymana w t=10 sekund, usługi Health mogą nie przesłać aktualizacji PAUSED do aplikacji, dopóki nie osiągnie ona wartości t=10,2 sekundy.

Praca z danymi z ExerciseClient

Dane dotyczące typów danych, które zostały zarejestrowane przez aplikację, są dostarczane w wiadomościach ExerciseUpdate.

Przetwarzacz wysyła wiadomości tylko wtedy, gdy jest aktywny lub gdy osiągnie maksymalny okres raportowania, np. co 150 sekund. Nie polegaj na częstotliwości ExerciseUpdate, aby przesuwać zegar activeDuration. Przykład implementacji niezależnego chronometru znajdziesz w przykładowym ćwiczeniu na GitHubie.

Gdy użytkownik rozpoczyna trening, ExerciseUpdate wiadomości mogą być wysyłane często, np. co sekundę. Gdy użytkownik rozpocznie trening, ekran może się wyłączyć. Usługi zdrowotne mogą wtedy przesyłać dane rzadziej, ale nadal z tą samą częstotliwością, aby uniknąć budzenia procesora głównego. Gdy użytkownik spojrzy na ekran, wszystkie dane, które są w trakcie grupowania, zostaną natychmiast dostarczone do aplikacji.

Kontrolowanie szybkości grupowania

W niektórych przypadkach możesz chcieć kontrolować częstotliwość, z jaką aplikacja otrzymuje określone typy danych, gdy ekran jest wyłączony. Obiekt BatchingMode pozwala aplikacji zastąpić domyślne zachowanie grupowania, aby częściej otrzymywać dane.

Aby skonfigurować szybkość przetwarzania w partiach:

  1. Sprawdź, czy urządzenie obsługuje określoną definicję 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 obiektu 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, zamiast utrzymywać określonego zachowania grupowania przez cały czas trwania treningu:

    val desiredModes = setOf(BatchingMode.HEART_RATE_5_SECONDS)
    exerciseClient.overrideBatchingModesForActiveExercise(desiredModes)
    
  4. Aby wyczyścić niestandardową wartość BatchingMode i przywrócić działanie domyślne, prześlij pusty zbiór do funkcji exerciseClient.overrideBatchingModesForActiveExercise().

Sygnatury czasowe

Punkt w czasie każdego punktu danych odpowiada czasowi od momentu uruchomienia urządzenia. Aby przekonwertować go na sygnaturę czasową:

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

Następnie można użyć tej wartości z getStartInstant() lub getEndInstant() dla każdego punktu danych.

Dokładność danych

Niektóre typy danych mogą zawierać informacje o dokładności powiązane z poszczególnymi punktami danych. Jest ona reprezentowana przez 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żyj właściwości accuracy, aby określić, czy każdy punkt danych ma wystarczającą dokładność dla Twojej aplikacji.

Przechowywanie i przesyłanie danych

Użyj Room, aby zachować dane dostarczane przez Health Services. Przesyłanie danych odbywa się na koniec ćwiczenia za pomocą mechanizmu takiego jak Menedżer zadań. Dzięki temu wywołania sieci służące do przesyłania danych są opóźniane do zakończenia ćwiczenia, co minimalizuje zużycie energii podczas ćwiczenia i upraszcza pracę.

Lista kontrolna integracji

Zanim opublikujesz aplikację, która korzysta z usług ExerciseClientzwiązanych ze zdrowiem, zapoznaj się z poniższą listą kontrolną, aby uniknąć typowych problemów. Sprawdź, czy:

  • Aplikacja sprawdza możliwości typu ćwiczenia i możliwości urządzenia za każdym razem, gdy jest uruchamiana. Dzięki temu możesz wykryć, kiedy dane urządzenie lub ćwiczenie nie obsługuje jednego z typów danych, których potrzebuje Twoja aplikacja.
  • Musisz poprosić o wymagane uprawnienia, zachować je i wymienić je w pliku manifestu. Przed wywołaniem funkcji prepareExerciseAsync() aplikacja potwierdza, że uprawnienia w czasie działania zostały przyznane.
  • Aplikacja używa uprawnień getCurrentExerciseInfoAsync(), aby obsługiwać przypadki, w których:
    • Ćwiczenie jest już śledzone, a aplikacja zastąpi poprzednie ćwiczenie.
    • Inna aplikacja zakończyła ćwiczenie. Może się tak zdarzyć, gdy użytkownik ponownie otworzy aplikację i zobaczy komunikat informujący, że ćwiczenie zostało przerwane, ponieważ przejęła je inna aplikacja.
  • Jeśli używasz danych LOCATION:
    • Aplikacja utrzymuje ForegroundService z odpowiednim foregroundServiceType przez cały czas wykonywania ćwiczenia (w tym podczas przygotowywania wywołania).
    • Sprawdzanie, czy GPS jest włączony na urządzeniu za pomocą isProviderEnabled(LocationManager.GPS_PROVIDER), i w razie potrzeby wyświetlanie użytkownikowi prośby o otwarcie ustawień lokalizacji.
    • W wymagających zastosowaniach, w których bardzo ważne jest otrzymywanie danych o lokalizacji z niskim opóźnieniem, rozważ zintegrowanie połączonego dostawcy lokalizacji (FLP) i używanie jego danych jako początkowego wyznaczenia lokalizacji. Jeśli z usługi Health Services są dostępne bardziej stabilne informacje o lokalizacji, użyj ich zamiast FLP.
  • Jeśli aplikacja wymaga przesyłania danych, wszystkie wywołania sieci do przesyłania danych są odkładane do czasu zakończenia ćwiczenia. W pozostałym czasie aplikacja oszczędnie korzysta z wywołań sieciowych.