Gesundheits- und Fitnessfunktionen deiner App durch die Erweiterung auf Wearables verbessern Geräte mit Wear OS.
Wear OS-Modul hinzufügen
Android Studio bietet einen praktischen Assistenten, mit dem du deiner App ein Wear OS-Modul hinzufügen kannst. In das Menü Datei > Neues Modul die Option Wear OS aus, wie im Folgenden gezeigt. Bild:
<ph type="x-smartling-placeholder">Wichtig: Die Mindest-SDK-Version muss API 30 oder höher sein. damit Sie die neueste Version von Health Services verwenden können. Gesundheitsdienste vereinfacht das Tracking von Messwerten und das Aufzeichnen von Daten durch Konfigurieren des Zustands automatisch Sensoren an.
Synchronisieren Sie nach Abschluss des Assistenten Ihr Projekt. Die folgende Run Konfiguration angezeigt:
<ph type="x-smartling-placeholder">So kannst du das Wear OS-Modul auf einem Wearable-Gerät ausführen. Es gibt zwei Möglichkeiten:
Führen Sie einen Emulator aus.
Die App muss auf einem echten Gerät ausgeführt werden.
Wenn Sie die Konfiguration ausführen, wird die App im Wear OS-Emulator bereitgestellt. und zeigt eine „Hello World“- Nutzererfahrung. Dies ist die grundlegende Einrichtung der Benutzeroberfläche mit Schreibe eine Nachricht für Wear OS, um deine App zu nutzen.
Gesundheitsdienstleistungen und Fitbit hinzufügen
Binde die folgenden Bibliotheken in dein Wear OS-Modul ein:
- Gesundheitsdienste:ermöglicht den Zugriff auf Sensoren und Daten auf der Smartwatch sehr praktisch und energieeffizienter.
- Hilt:Ermöglicht eine effektive Abhängigkeitsinjektion und -verwaltung.
Health Services Manager erstellen
Um die Nutzung von Gesundheitsdiensten zu vereinfachen und eine kleinere und reibungsloseren API arbeiten, 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 mit dem folgenden Snippet erstellt haben:
@Module
@InstallIn(SingletonComponent::class)
internal object DataModule {
@Provides
@Singleton
fun provideHealthServices(@ApplicationContext context: Context): HealthServicesManager = HealthServicesManager(context)
}
Sie können die HealthServicesManager
wie jede andere Hilt-Abhängigkeit injizieren.
Die neue HealthServicesManager
bietet eine heartRateMeasureFlow()
-Methode, die
registriert einen Listener für den Herzmonitor und gibt die empfangenen Daten aus.
Datenupdates auf Wearable-Geräten aktivieren
Für die Aktualisierung von Fitnessdaten ist die Berechtigung BODY_SENSORS
erforderlich. Wenn Sie
noch nicht getan haben, deklarieren Sie die Berechtigung BODY_SENSORS
in Ihrem
in der Manifestdatei der App. 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. Auf vorheriger können Sie den Datenstream des Sensors simulieren. In einem Terminal führen Sie diesen ADB-Befehl aus:
adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices
Probiere verschiedene Übungen aus, um verschiedene Herzfrequenzwerte zu sehen. Dieser Befehl simuliert den 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
Führen Sie den folgenden Befehl aus, um die Simulation der Daten zu beenden:
adb shell am broadcast -a \
"whs.USE_SENSOR_PROVIDERS" \
com.google.android.wearable.healthservices
Herzfrequenzdaten lesen
Mit der Berechtigung BODY_SENSORS
kannst du die Herzfrequenz des Benutzers lesen
(heartRateMeasureFlow()
) in HealthServicesManager
. In der App
Auf der Benutzeroberfläche wird der aktuelle Herzfrequenzwert angezeigt, der vom Sensor am
Wearable.
Fang in deinem ViewModel
an, Daten mithilfe des Herzfrequenz-Flussobjekts zu erfassen.
Dies wird 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 Benutzeroberfläche Ihrer App:
val heartRate by viewModel.hr
Text(
text = "Heart Rate: $heartRate",
style = MaterialTheme.typography.display1
)
Daten an ein Handheld-Gerät senden
Wenn du Gesundheits- und Fitnessdaten an ein Mobilgerät senden möchtest, verwende die DataClient
im Bereich Gesundheitsdienstleistungen. Im folgenden Code-Snippet sehen Sie, wie ein Herz
zuvor von deiner App erhobene Daten bewerten:
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
Um die Daten auf dem Smartphone zu empfangen, erstelle einen
WearableListenerService
:
@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
}
}
}
}
}
Beachten Sie nach Abschluss dieses Schritts einige interessante Details:
- Mit der Annotation
@AndroidEntryPoint
können wir Hilt in dieser Klasse verwenden. - Das
@Inject lateinit var heartRateMonitor: HeartRateMonitor
wird tatsächlich Abhängigkeit in diese Klasse einfügen - Die Klasse implementiert
onDataChanged()
und empfängt eine Sammlung von Ereignissen, die können Sie parsen und
Mit der folgenden HeartRateMonitor
-Logik kannst du die empfangene Herzfrequenz
in einen anderen Teil der Codebasis Ihrer App einfügen:
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 auf
für Datenbeobachter mit SharedFlow
zur Verfügung.
Das letzte Bit ist die Deklaration von Service
in der Telefon-App.
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
In den Teil deiner App, der auf einem Handheld-Gerät ausgeführt wird, fügst du den
HeartRateMonitor
in den Konstruktor Ihres Ansichtsmodells. Dieses HeartRateMonitor
-Objekt die Herzfrequenzdaten und gibt bei Bedarf Aktualisierungen der Benutzeroberfläche aus.