Verbessere die Gesundheits- und Fitnessfunktionen deiner App, indem du sie auf Wearable-Geräte mit Wear OS ausweitest.
Wear OS-Modul hinzufügen
In Android Studio gibt es einen praktischen Assistenten, mit dem Sie Ihrer App ein Wear OS-Modul hinzufügen können. Wählen Sie im Menü File > New Module (Datei > Neues Modul) die Option Wear OS aus, wie in der folgenden Abbildung gezeigt:
Wichtig: Die Mindestens erforderliche SDK-Version muss API 30 oder höher sein, damit Sie die neueste Version von Health Services nutzen können. Health Services erleichtert das Verfolgen von Messwerten und Aufzeichnen von Daten durch automatische Konfiguration von Gesundheitssensoren.
Nachdem Sie den Assistenten abgeschlossen haben, synchronisieren Sie Ihr Projekt. Die folgende Ausführungskonfiguration wird angezeigt:
So lässt sich das Wear OS-Modul auf einem Wearable-Gerät ausführen. Es gibt zwei Möglichkeiten:
Ausführung in einem Emulator.
die auf einem echten Gerät ausgeführt werden.
Wenn Sie die Konfiguration ausführen, wird die App für den Wear OS-Emulator oder das Wear OS-Gerät bereitgestellt und es wird ein „Hallo Welt“-Erlebnis angezeigt. Dies ist die grundlegende Einrichtung der Benutzeroberfläche mit Composer für Wear OS, um Ihre App zu erstellen.
Gesundheitsdienste und Handgriff hinzufügen
Binde die folgenden Bibliotheken in dein Wear OS-Modul ein:
- Gesundheitsdienste:Ermöglicht den Zugriff auf Sensoren und Daten der Uhr sehr bequem und energieeffizienter.
- Hilt:Ermöglicht eine effektive Injektion und Verwaltung von Abhängigkeiten.
Manager für Gesundheitsdienste erstellen
Um die Verwendung von Health Services zu vereinfachen und eine kleinere und reibungslosere API bereitzustellen, können Sie einen Wrapper wie den folgenden erstellen:
private const val TAG = "WATCHMAIN"
class HealthServicesManager(context: Context) {
private val measureClient = HealthServices.getClient(context).measureClient
suspend fun hasHeartRateCapability() = runCatching {
val capabilities = measureClient.getCapabilities()
(DataType.HEART_RATE_BPM in capabilities.supportedDataTypesMeasure)
}.getOrDefault(false)
/**
* Returns a cold flow. When activated, the flow will register a callback for heart rate data
* and start to emit messages. When the consuming coroutine is canceled, the measure callback
* is unregistered.
*
* [callbackFlow] creates a bridge between a callback-based API and Kotlin flows.
*/
@ExperimentalCoroutinesApi
fun heartRateMeasureFlow(): Flow<MeasureMessage> = callbackFlow {
val callback = object : MeasureCallback {
override fun onAvailabilityChanged(dataType: DeltaDataType<*, *>, availability: Availability) {
// Only send back DataTypeAvailability (not LocationAvailability)
if (availability is DataTypeAvailability) {
trySendBlocking(MeasureMessage.MeasureAvailability(availability))
}
}
override fun onDataReceived(data: DataPointContainer) {
val heartRateBpm = data.getData(DataType.HEART_RATE_BPM)
Log.d(TAG, "💓 Received heart rate: ${heartRateBpm.first().value}")
trySendBlocking(MeasureMessage.MeasureData(heartRateBpm))
}
}
Log.d(TAG, "⌛ Registering for data...")
measureClient.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)
awaitClose {
Log.d(TAG, "👋 Unregistering for data")
runBlocking {
measureClient.unregisterMeasureCallback(DataType.HEART_RATE_BPM, callback)
}
}
}
}
sealed class MeasureMessage {
class MeasureAvailability(val availability: DataTypeAvailability) : MeasureMessage()
class MeasureData(val data: List<SampleDataPoint<Double>>) : MeasureMessage()
}
Nachdem Sie das Hilt-Modul zu dessen Verwaltung erstellt haben, verwenden Sie das folgende Snippet:
@Module
@InstallIn(SingletonComponent::class)
internal object DataModule {
@Provides
@Singleton
fun provideHealthServices(@ApplicationContext context: Context): HealthServicesManager = HealthServicesManager(context)
}
können Sie das HealthServicesManager
als jede andere Hilt-Abhängigkeit einschleusen.
Die neue HealthServicesManager
bietet eine heartRateMeasureFlow()
-Methode, die einen Listener für den Herzmonitor registriert und die empfangenen Daten ausgibt.
Datenupdates auf Wearable-Geräten aktivieren
Für die Aktualisierung von Fitnessdaten ist die Berechtigung „BODY_SENSORS
“ erforderlich. Du musst die Berechtigung BODY_SENSORS
in der Manifestdatei deiner App deklarieren, falls noch nicht geschehen. Fordern Sie dann die Berechtigung an, wie in diesem Snippet gezeigt:
val permissionState = rememberPermissionState(
permission = Manifest.permission.BODY_SENSORS,
onPermissionResult = { granted -> /* do something */ }
)
[...]
if (permissionState.status.isGranted) {
// do something
} else {
permissionState.launchPermissionRequest()
}
Wenn Sie Ihre App auf einem physischen Gerät testen, sollten die Daten aktualisiert werden.
Ab Wear OS 4 zeigen Emulatoren auch automatisch Testdaten an. In früheren Versionen können Sie den Datenstream des Sensors simulieren. Führen Sie in einem Terminalfenster den folgenden ADB-Befehl aus:
adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices
Simuliere verschiedene Übungen, um unterschiedliche Herzfrequenzwerte zu sehen. Dieser Befehl simuliert das Gehen:
adb shell am broadcast \
-a "whs.synthetic.user.START_WALKING" \
com.google.android.wearable.healthservices
Dieser Befehl simuliert die Ausführung:
adb shell am broadcast \
-a "whs.synthetic.user.START_RUNNING" \
com.google.android.wearable.healthservices
Um die Simulation der Daten zu beenden, führen Sie den folgenden Befehl aus:
adb shell am broadcast -a \
"whs.USE_SENSOR_PROVIDERS" \
com.google.android.wearable.healthservices
Herzfrequenzdaten lesen
Mit der Berechtigung BODY_SENSORS
können Sie die Herzfrequenz des Nutzers (heartRateMeasureFlow()
) im HealthServicesManager
ablesen. Auf der Benutzeroberfläche der Wear OS-App wird der aktuelle Herzfrequenzwert angezeigt, der vom Sensor des Wearable-Geräts gemessen wird.
Beginnen Sie in Ihrem ViewModel
mit der Erfassung von Daten mithilfe des Herzfrequenz-Flow-Objekts, wie im folgenden Snippet gezeigt:
val hr: MutableState<Double> = mutableStateOf(0.0)
[...]
healthServicesManager
.heartRateMeasureFlow()
.takeWhile { enabled.value }
.collect { measureMessage ->
when (measureMessage) {
is MeasureData -> {
val latestHeartRateValue = measureMessage.data.last().value
hr.value = latestHeartRateValue
}
is MeasureAvailability -> availability.value =
measureMessage.availability
}
}
Verwenden Sie ein zusammensetzbares Objekt ähnlich dem folgenden, um die Live-Daten in der UI Ihrer App anzuzeigen:
val heartRate by viewModel.hr
Text(
text = "Heart Rate: $heartRate",
style = MaterialTheme.typography.display1
)
Daten an ein Handheld-Gerät senden
Verwenden Sie die Klasse DataClient
in Health Services, um Gesundheits- und Fitnessdaten an ein Handheld-Gerät zu senden. Das folgende Code-Snippet zeigt, wie zuvor von deiner App erfasste Herzfrequenzdaten gesendet werden:
class HealthServicesManager(context: Context) {
private val dataClient by lazy { Wearable.getDataClient(context) }
[...]
suspend fun sendToHandheldDevice(heartRate: Int) {
try {
val result = dataClient
.putDataItem(PutDataMapRequest
.create("/heartrate")
.apply { dataMap.putInt("heartrate", heartRate) }
.asPutDataRequest()
.setUrgent())
.await()
Log.d(TAG, "DataItem saved: $result")
} catch (cancellationException: CancellationException) {
throw cancellationException
} catch (exception: Exception) {
Log.d(TAG, "Saving DataItem failed: $exception")
}
}
}
Daten auf dem Smartphone empfangen
Erstellen Sie einen WearableListenerService
, um die Daten auf dem Smartphone zu empfangen:
@AndroidEntryPoint
class DataLayerListenerService : WearableListenerService() {
@Inject
lateinit var heartRateMonitor: HeartRateMonitor
override fun onDataChanged(dataEvents: DataEventBuffer) {
dataEvents.forEach { event ->
when (event.type) {
DataEvent.TYPE_CHANGED -> {
event.dataItem.run {
if (uri.path?.compareTo("/heartrate") == 0) {
val heartRate = DataMapItem.fromDataItem(this)
.dataMap.getInt(HR_KEY)
Log.d("DataLayerListenerService",
"New heart rate value received: $heartRate")
heartRateMonitor.send(heartRate)
}
}
}
DataEvent.TYPE_DELETED -> {
// DataItem deleted
}
}
}
}
}
Nachdem Sie diesen Schritt abgeschlossen haben, finden Sie einige interessante Details:
- Mit der Annotation
@AndroidEntryPoint
können wir Hilt in dieser Klasse verwenden. @Inject lateinit var heartRateMonitor: HeartRateMonitor
schleust tatsächlich eine Abhängigkeit in diese Klasse ein.- Die Klasse implementiert
onDataChanged()
und empfängt eine Sammlung von Ereignissen, die Sie parsen und verwenden können.
Mit der folgenden HeartRateMonitor
-Logik kannst du die empfangenen Herzfrequenzwerte an einen anderen Teil der Codebasis deiner App senden:
class HeartRateMonitor {
private val datapoints = MutableSharedFlow<Int>(extraBufferCapacity = 10)
fun receive(): SharedFlow<Int> = datapoints.asSharedFlow()
fun send(hr: Int) {
datapoints.tryEmit(hr)
}
}
Ein Datenbus empfängt die Ereignisse von der Methode onDataChanged()
und stellt sie Datenbeobachtern mithilfe einer SharedFlow
zur Verfügung.
Das letzte Bit ist die Deklaration von Service
in der Telefonanwendung AndroidManifest.xml
:
<service
android:name=".DataLayerListenerService"
android:exported="true">
<intent-filter>
<!-- listeners receive events that match the action and data filters -->
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data
android:host="*"
android:pathPrefix="/heartrate"
android:scheme="wear" />
</intent-filter>
</service>
Echtzeitdaten auf einem Handheld-Gerät anzeigen
Fügen Sie in dem Teil der App, der auf einem Handheld-Gerät ausgeführt wird, das HeartRateMonitor
in den Konstruktor des Ansichtsmodells ein. Dieses HeartRateMonitor
-Objekt überwacht die Herzfrequenzdaten und gibt bei Bedarf Aktualisierungen der Benutzeroberfläche aus.