Training mit TrainClient aufzeichnen

Health Services bietet über die ExerciseClient erstklassigen Support für Trainings-Apps. Mit ExerciseClient kann deine App steuern, wann eine Übung läuft, Trainingsziele hinzufügen und Benachrichtigungen zum Trainingsstatus, zu Trainingsereignissen oder zu anderen gewünschten Messwerten erhalten. Weitere Informationen findest du in der vollständigen Liste der von Health Services unterstützten Trainingstypen.

Sehen Sie sich das Übungsbeispiel auf GitHub an.

Abhängigkeiten hinzufügen

Zum Hinzufügen einer Abhängigkeit von Health Services müssen Sie Ihrem Projekt das Maven-Repository von Google hinzufügen. Weitere Informationen finden Sie unter Maven-Repository von Google.

Fügen Sie dann in der Datei build.gradle auf Modulebene die folgende Abhängigkeit hinzu:

Groovig

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

Kotlin

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

App-Struktur

Verwenden Sie die folgende App-Struktur, wenn Sie eine Trainings-App mit Health Services erstellen:

Bei der Vorbereitung auf ein Training und während des Trainings kann deine Aktivität aus verschiedenen Gründen angehalten werden. Die Nutzenden können zu einer anderen App wechseln oder zum Zifferblatt zurückkehren. Es kann sein, dass über deiner Aktivität etwas angezeigt wird oder der Bildschirm nach einer bestimmten Zeit der Inaktivität ausgeschaltet wird. Verwenden Sie ein kontinuierlich ausgeführtes ForegroundService in Verbindung mit ExerciseClient, damit das gesamte Training ordnungsgemäß funktioniert.

Wenn du eine ForegroundService verwendest, kannst du die Ongoing Activity API verwenden, um eine Anzeige auf deiner Smartwatch-Oberfläche anzuzeigen, damit der Nutzer schnell zum Training zurückkehren kann.

Es ist wichtig, dass Sie Standortdaten in Ihrem Dienst im Vordergrund angemessen anfordern. Geben Sie in der Manifestdatei foregroundServiceType="location" und die entsprechenden Berechtigungen an.

Verwende AmbientLifecycleObserver für deine Aktivität vor dem Training, die den Aufruf prepareExercise() enthält, und für deine Trainingsaktivität. Allerdings solltest du das Display während des Trainings nicht während des Inaktivmodus aktualisieren: Dies liegt daran, dass Health Services Trainingsdaten bündelt, wenn sich der Gerätebildschirm im Inaktivmodus befindet, um Strom zu sparen. Die angezeigten Informationen sind daher möglicherweise nicht aktuell. Zeigen Sie während des Trainings Daten an, die für den Nutzer sinnvoll sind, indem entweder aktuelle Informationen oder ein leerer Bildschirm angezeigt wird.

Funktionen prüfen

Jedes ExerciseType unterstützt bestimmte Datentypen für Messwerte und Trainingsziele. Prüfe diese Funktionen beim Start, da sie je nach Gerät variieren können. Es kann sein, dass ein Gerät einen bestimmten Trainingstyp nicht unterstützt oder eine bestimmte Funktion wie die automatische Pause nicht. Außerdem können sich die Funktionen eines Geräts im Laufe der Zeit ändern, z. B. nach einem Softwareupdate.

Fragen Sie beim Start der App die Gerätefunktionen ab und speichern und verarbeiten Sie Folgendes:

  • Die von der Plattform unterstützten Übungen.
  • Die Funktionen, die für das jeweilige Training unterstützt werden.
  • Die für das jeweilige Training unterstützten Datentypen.
  • Die für jeden dieser Datentypen erforderlichen Berechtigungen.

Verwenden Sie ExerciseCapabilities.getExerciseTypeCapabilities() für den gewünschten Trainingstyp, um zu sehen, welche Art von Messwerten Sie anfordern können, welche Trainingsziele Sie konfigurieren können und welche anderen Funktionen für diesen Typ verfügbar sind. Dies wird im folgenden Beispiel veranschaulicht:

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)
    }
}

Im zurückgegebenen ExerciseTypeCapabilities werden in supportedDataTypes die Datentypen aufgelistet, für die Sie Daten anfordern können. Das variiert je nach Gerät. Achten Sie deshalb darauf, kein nicht unterstütztes DataType anzufordern, da die Anfrage sonst fehlschlagen könnte.

Mit den Feldern supportedGoals und supportedMilestones können Sie bestimmen, ob die Übung ein Trainingsziel unterstützen kann, das Sie erstellen möchten.

Wenn Nutzer in Ihrer App die automatische Pause verwenden können, müssen Sie mit supportsAutoPauseAndResume prüfen, ob diese Funktion vom Gerät unterstützt wird. ExerciseClient lehnt Anfragen ab, die auf dem Gerät nicht unterstützt werden.

Im folgenden Beispiel wird die Unterstützung für den Datentyp HEART_RATE_BPM, die Zielfunktion STEPS_TOTAL und die Funktion für die automatische Pause geprüft:

// 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

Für Updates zum Trainingsstatus registrieren

Trainings-Updates werden an den Hörer gesendet. Ihre Anwendung kann jeweils nur einen Listener registrieren. Richten Sie Ihren Listener ein, bevor Sie das Training starten, wie im folgenden Beispiel gezeigt. Ihr Listener erhält nur Updates zu Übungen, die Ihrer App gehören.

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)

Trainingslebensdauer verwalten

Gesundheitsdienste unterstützen in allen Anwendungen auf dem Gerät jeweils maximal eine Übung. Wenn eine Übung aufgezeichnet wird und eine andere App beginnt, ein neues Training zu verfolgen, wird die erste Übung beendet.

Bevor du mit dem Training beginnst, solltest du Folgendes tun:

  • Überprüfe, ob ein Training bereits getrackt wird, und reagiere entsprechend. Bitten Sie beispielsweise den Nutzer um Bestätigung, bevor Sie eine vorherige Übung überschreiben und beginnen, eine neue zu erfassen.

Das folgende Beispiel zeigt, wie Sie mit getCurrentExerciseInfoAsync nach einer vorhandenen Übung suchen:

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.
    }
}

Berechtigungen

Wenn Sie ExerciseClient verwenden, muss Ihre Anwendung die erforderlichen Berechtigungen anfordern und beibehalten. Wenn Ihre App LOCATION-Daten verwendet, muss sie dafür sorgen, dass sie die entsprechenden Berechtigungen anfordert und beibehält.

Führen Sie für alle Datentypen die folgenden Schritte aus, bevor Sie prepareExercise() oder startExercise() aufrufen:

  • Geben Sie die entsprechenden Berechtigungen für die angeforderten Datentypen in der Datei AndroidManifest.xml an.
  • Überprüfen Sie, ob der Nutzer die erforderlichen Berechtigungen erteilt hat. Weitere Informationen finden Sie unter App-Berechtigungen anfordern. Health Services lehnt die Anfrage ab, wenn die erforderlichen Berechtigungen noch nicht gewährt wurden.

Führen Sie für Standortdaten die folgenden zusätzlichen Schritte aus:

Auf ein Training vorbereiten

Einige Sensoren, z. B. GPS oder Herzfrequenz, benötigen möglicherweise kurze Zeit zum Aufwärmen. Außerdem kann es sein, dass der Nutzer seine Daten sehen möchte, bevor er mit dem Training beginnt. Mit der optionalen Methode prepareExerciseAsync() können sich diese Sensoren aufwärmen und Daten empfangen, ohne den Timer für das Training zu starten. Der activeDuration ist von dieser Vorbereitungszeit nicht betroffen.

Prüfen Sie Folgendes, bevor Sie prepareExerciseAsync() aufrufen:

  • Prüfen Sie die plattformweite Standorteinstellung. Der Nutzer legt diese Einstellung im Hauptmenü der Einstellungen fest. Sie unterscheidet sich von der Berechtigungsprüfung auf App-Ebene.

    Wenn die Einstellung deaktiviert ist, benachrichtigen Sie den Nutzer, dass er den Zugriff auf den Standort verweigert hat. Fordern Sie ihn auf, ihn zu aktivieren, wenn Ihre App Standort benötigt.

  • Prüfen Sie, ob Ihre App Laufzeitberechtigungen für Körpersensoren, Aktivitätserkennung und den genauen Standort hat. Bei fehlenden Berechtigungen müssen Sie den Nutzer zur Eingabe von Laufzeitberechtigungen auffordern und angemessenen Kontext angeben. Wenn der Nutzer eine bestimmte Berechtigung nicht gewährt, entfernen Sie die mit dieser Berechtigung verknüpften Datentypen aus dem Aufruf von prepareExerciseAsync(). Wenn weder die Berechtigung „Körpersensor“ noch die Berechtigung zur Standortermittlung erteilt wurde, rufe prepareExerciseAsync() nicht auf, da der Vorbereitungsaufruf vor dem Start eines Trainings speziell dafür dient, eine stabile Herzfrequenz- oder GPS-Standortbestimmung zu erzielen. Die App kann weiterhin schrittbasierte Messwerte wie Entfernung, Tempo, Geschwindigkeit und andere Messwerte abrufen, für die diese Berechtigungen nicht erforderlich sind.

So sorgen Sie dafür, dass der Aufruf von prepareExerciseAsync() erfolgreich ist:

  • Verwenden Sie AmbientLifecycleObserver für die Aktivität vor dem Training, die den Vorbereitungsanruf enthält.
  • Rufen Sie prepareExerciseAsync() über den Dienst im Vordergrund auf. Wenn sie sich nicht in einem Dienst befindet und an den Aktivitätslebenszyklus gebunden ist, wird die Sensorvorbereitung möglicherweise unnötigerweise abgebrochen.
  • Rufe endExercise() auf, um die Sensoren auszuschalten und den Stromverbrauch zu reduzieren, wenn der Nutzer die Aktivität vor dem Training verlässt.

Das folgende Beispiel zeigt, wie prepareExerciseAsync() aufgerufen wird:

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.

Sobald die App den Status PREPARING hat, werden Updates zur Sensorverfügbarkeit im ExerciseUpdateCallback bis onAvailabilityChanged() gesendet. Diese Informationen können dem Nutzer dann angezeigt werden, damit er entscheiden kann, ob er mit dem Training beginnen möchte.

Starte das Training

Wenn Sie eine Übung starten möchten, erstellen Sie einen ExerciseConfig, um den Trainingstyp, die Datentypen, für die Sie Messwerte erhalten möchten, und alle Trainingsziele oder Meilensteine zu konfigurieren.

Trainingsziele bestehen aus einer DataType und einer Bedingung. Trainingsziele sind ein einmaliges Ziel, das ausgelöst wird, wenn eine Bedingung erfüllt ist, z. B. wenn der Nutzer eine bestimmte Strecke läuft. Es kann auch ein Übungs- Meilenstein festgelegt werden. Trainingsmeilensteine können mehrmals ausgelöst werden, z. B. jedes Mal, wenn der Nutzer einen bestimmten Punkt über die festgelegte Strecke hinaus läutet.

Im folgenden Beispiel wird gezeigt, wie Sie für jeden Typ ein Zielvorhaben erstellen:

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()
}

Du kannst auch Runden für alle Trainings markieren. Health Services stellt eine ExerciseLapSummary mit Messwerten bereit, die über den Rundenzeitraum aggregiert wurden.

Das vorherige Beispiel zeigt die Verwendung von isGpsEnabled. Das muss „true“ sein, wenn Standortdaten angefordert werden. Die Verwendung von GPS kann jedoch auch bei anderen Messwerten hilfreich sein. Wenn ExerciseConfig die Entfernung als DataType angibt, werden standardmäßig Schritte zur Schätzung der Entfernung verwendet. Wenn GPS optional aktiviert ist, kann stattdessen Standortinformationen zur Schätzung der Entfernung verwendet werden.

Ein Training pausieren, fortsetzen und beenden

Sie können Workouts mit der entsprechenden Methode wie pauseExerciseAsync() oder endExerciseAsync() pausieren, fortsetzen und beenden.

Verwenden Sie den Status aus ExerciseUpdate als zentrale Informationsquelle. Das Training gilt nicht als pausiert, wenn der Aufruf von pauseExerciseAsync() zurückgegeben wird, sondern wenn dieser Status in der ExerciseUpdate-Nachricht widergespiegelt wird. Dies ist besonders wichtig, wenn es um UI-Zustände geht. Wenn der Nutzer „Pause“ drückt, deaktiviere die Pause-Schaltfläche und rufe pauseExerciseAsync() bei Gesundheitsdiensten auf. Warten Sie, bis die Gesundheitsdienste mit ExerciseUpdate.exerciseStateInfo.state den Status „Pausiert“ erreicht haben, und wechseln Sie dann die Schaltfläche zum Fortsetzen. Das liegt daran, dass die Übermittlung von Statusaktualisierungen für Gesundheitsdienste länger dauern kann als das Drücken der Schaltfläche. Wenn Sie also alle Änderungen an der Benutzeroberfläche mit dem Betätigen der Schaltfläche verknüpfen, kann die Benutzeroberfläche mit dem Status der Gesundheitsdienste nicht mehr synchron sein.

Beachten Sie dies in den folgenden Situationen:

  • Automatische Pause ist aktiviert: Das Training kann ohne Nutzerinteraktion angehalten oder gestartet werden.
  • Eine andere App startet ein Training: Ihr Training wird möglicherweise ohne Nutzerinteraktion beendet.

Wenn das Training Ihrer App von einer anderen App beendet wird, muss Ihre App die Beendigung ordnungsgemäß verarbeiten:

  • Speichere den Teil-Trainingsstatus, damit der Fortschritt eines Benutzers nicht gelöscht wird.
  • Entferne das Symbol „Laufende Aktivität“ und sende dem Nutzer eine Benachrichtigung, in der er darüber informiert wird, dass sein Training von einer anderen App beendet wurde.

Gehen Sie auch auf den Fall ein, dass Berechtigungen während einer laufenden Übung widerrufen werden. Dieses wird mit dem Status isEnded mit dem ExerciseEndReason AUTO_END_PERMISSION_LOST gesendet. Behandeln Sie diesen Fall ähnlich wie den Beendigungsfall: Speichern Sie den Teilstatus, entfernen Sie das Symbol für laufende Aktivitäten und senden Sie eine Benachrichtigung darüber, was dem Nutzer passiert ist.

Das folgende Beispiel zeigt, wie Sie korrekt auf Beendigung prüfen können:

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
        }
        ...
    }
    ...
}

Dauer der aktiven Nutzung verwalten

Während eines Trainings kann eine App die aktive Dauer des Trainings anzeigen. Die App, die Gesundheitsdienste und die Micro Controller Unity (MCU) des Geräts – der energiesparende Prozessor, der für die Trainingsaufzeichnung verantwortlich ist – müssen alle mit derselben aktuellen aktiven Dauer synchron sein. Zu diesem Zweck sendet Health Services ein ActiveDurationCheckpoint, das einen Ankerpunkt enthält, von dem aus die App ihren Timer starten kann.

Da die aktive Dauer vom MCU gesendet wird und etwas dauern kann, bis sie in der Anwendung eintrifft, enthält ActiveDurationCheckpoint zwei Attribute:

  • activeDuration: wie lange das Training aktiv war
  • time: Zeitpunkt, zu dem die aktive Dauer berechnet wurde

Daher kann in der Anwendung die aktive Dauer einer Übung aus ActiveDurationCheckpoint anhand der folgenden Gleichung berechnet werden:

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

Dies berücksichtigt die kleine Differenz zwischen der Berechnung der aktiven Dauer auf der MCU und der Ankunft in der App. Dies kann verwendet werden, um ein Chronometer in die App einzubinden und sicherzustellen, dass der Timer der App perfekt auf die Zeit in Health Services und der MCU abgestimmt ist.

Wenn das Training pausiert ist, wartet die App darauf, den Timer in der Benutzeroberfläche neu zu starten, bis die berechnete Zeit abgelaufen ist, was auf der Benutzeroberfläche angezeigt wird. Das liegt daran, dass das Pausensignal die Gesundheitsdienste und die MCU mit einer leichten Verzögerung erreicht. Wenn die Anwendung beispielsweise bei t=10 Sekunden pausiert wird, stellt Health Services das PAUSED-Update möglicherweise erst nach t=10, 2 Sekunden an die Anwendung weiter.

Mit Daten von TrainingClient arbeiten

Messwerte für die Datentypen, für die Ihre Anwendung registriert wurde, werden in ExerciseUpdate-Nachrichten zugestellt.

Der Prozessor sendet nur dann Nachrichten, wenn er aktiv ist oder wenn ein maximaler Berichtszeitraum erreicht ist, z. B. alle 150 Sekunden. Verlasse dich nicht auf die ExerciseUpdate-Frequenz, um einen Chronometer mit dem activeDuration voranzubringen. Im Übungsbeispiel auf GitHub finden Sie ein Beispiel für die Implementierung eines unabhängigen Chronometers.

Wenn ein Nutzer ein Training startet, können ExerciseUpdate-Nachrichten häufig zugestellt werden, z. B. jede Sekunde. Wenn der Nutzer mit dem Training beginnt, wird der Bildschirm möglicherweise ausgeschaltet. Gesundheitsdienste können Daten dann seltener senden, aber mit der gleichen Häufigkeit erfasst werden, um zu vermeiden, dass der Hauptprozessor aktiviert wird. Wenn der Nutzer auf den Bildschirm schaut, werden alle Daten, die gerade im Batch verarbeitet werden, sofort an Ihre App gesendet.

Batchverarbeitungsrate steuern

In einigen Szenarien möchten Sie möglicherweise steuern, wie oft Ihre Anwendung bestimmte Datentypen empfängt, während der Bildschirm ausgeschaltet ist. Mit einem BatchingMode-Objekt kann Ihre App das Standard-Batchingverhalten überschreiben, um Datenübermittlungen häufiger zu erhalten.

Führen Sie die folgenden Schritte aus, um die Batchverarbeitungsrate zu konfigurieren:

  1. Prüfe, ob die jeweilige BatchingMode-Definition vom Gerät unterstützt wird:

    // 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. Gib an, dass das ExerciseConfig-Objekt eine bestimmte BatchingMode verwenden soll, wie im folgenden Code-Snippet gezeigt.

    val config = ExerciseConfig(
        exerciseType = ExerciseType.WORKOUT,
        dataTypes = setOf(
            DataType.HEART_RATE_BPM,
            DataType.TOTAL_CALORIES
        ),
        // ...
        batchingModeOverrides = setOf(BatchingMode.HEART_RATE_5_SECONDS)
    )
    
  3. Optional können Sie BatchingMode während des Trainings dynamisch konfigurieren, anstatt ein bestimmtes Batch-Verhalten während der gesamten Dauer des Trainings beizubehalten:

    val desiredModes = setOf(BatchingMode.HEART_RATE_5_SECONDS)
    exerciseClient.overrideBatchingModesForActiveExercise(desiredModes)
    
  4. Wenn Sie das benutzerdefinierte BatchingMode löschen und zum Standardverhalten zurückkehren möchten, übergeben Sie einen leeren Satz an exerciseClient.overrideBatchingModesForActiveExercise().

Zeitstempel

Der Zeitpunkt jedes Datenpunkts steht für die Dauer seit dem Start des Geräts. So wandeln Sie dies in einen Zeitstempel um:

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

Dieser Wert kann dann mit getStartInstant() oder getEndInstant() für jeden Datenpunkt verwendet werden.

Datengenauigkeit

Einige Datentypen können Genauigkeitsinformationen enthalten, die mit jedem Datenpunkt verknüpft sind. Dies wird in der Property accuracy dargestellt.

Die Klassen HrAccuracy und LocationAccuracy können für die Datentypen HEART_RATE_BPM und LOCATION ausgefüllt werden. Verwenden Sie gegebenenfalls das Attribut accuracy, um zu bestimmen, ob jeder Datenpunkt eine ausreichende Genauigkeit für Ihre Anwendung hat.

Daten speichern und hochladen

Verwenden Sie Room, um von Gesundheitsdiensten bereitgestellte Daten dauerhaft zu speichern. Der Datenupload erfolgt am Ende der Übung mit einem Mechanismus wie Work Manager. Dadurch wird sichergestellt, dass Netzwerkaufrufe zum Hochladen von Daten bis zum Ende der Übung verschoben werden, wodurch der Stromverbrauch während des Trainings minimiert und die Arbeit vereinfacht wird.

Liste zur Überprüfung der Einbindung

Bevor du eine App veröffentlichst, die ExerciseClient von Gesundheitsdiensten verwendet, solltest du die folgende Checkliste durchgehen, um einige häufige Probleme zu vermeiden. Sind die folgenden Voraussetzungen erfüllt?

  • Ihre App prüft bei jeder Ausführung die Funktionen des Übungstyps und die Gerätefunktionen. Auf diese Weise kannst du erkennen, wenn ein bestimmtes Gerät oder eine bestimmte Übung einen der Datentypen, die deine App benötigt, nicht unterstützt.
  • Sie fordern die erforderlichen Berechtigungen an, verwalten sie und geben sie in Ihrer Manifestdatei an. Bevor prepareExerciseAsync() aufgerufen wird, bestätigt Ihre App, dass die Laufzeitberechtigungen gewährt wurden.
  • In Ihrer Anwendung wird getCurrentExerciseInfoAsync() für die Fälle verwendet, in denen Folgendes zutrifft:
    • Eine Übung wird bereits aufgezeichnet und deine App überschreibt die vorherige Übung.
    • Eine andere App hat dein Training beendet. Das kann passieren, wenn der Nutzer die App noch einmal öffnet und dann eine Meldung erhält, dass die Übung beendet wurde, weil eine andere App übernommen hat.
  • Wenn Sie LOCATION-Daten verwenden:
    • Deine App behält während der Dauer der Übung (einschließlich des Vorbereitungsaufrufs) ein ForegroundService mit dem entsprechenden foregroundServiceType bei.
    • Prüfe mit isProviderEnabled(LocationManager.GPS_PROVIDER), ob GPS auf dem Gerät aktiviert ist, und fordert den Nutzer gegebenenfalls auf, die Standorteinstellungen zu öffnen.
    • Für anspruchsvolle Anwendungsfälle, bei denen der Empfang von Standortdaten mit niedriger Latenz von großer Bedeutung ist, sollten Sie den FusedLocation Provider (FLP) einbinden und dessen Daten als anfängliche Standortbestimmung verwenden. Wenn stabilere Standortinformationen von Gesundheitsdiensten verfügbar sind, verwenden Sie diese anstelle von FLP.
  • Wenn für Ihre Anwendung das Hochladen von Daten erforderlich ist, werden alle Netzwerkaufrufe zum Hochladen von Daten bis zum Ende des Trainings ausgesetzt. Andernfalls führt Ihre Anwendung während der Übung nur sparsam alle notwendigen Netzwerkaufrufe aus.