Datenschichtereignisse für Wear verarbeiten

Wenn Sie einen Aufruf an die Data Layer API senden, können Sie den Status des Aufrufs nach Abschluss abrufen. Sie können auch auf Datenereignisse warten, die durch Datenänderungen entstehen, die Ihre App an einer beliebigen Stelle im Wear OS by Google-Netzwerk vornimmt.

Ein Beispiel für die effektive Verwendung der Data Layer API finden Sie in der Android Beispiel-App DataLayer.

Auf den Status von Data Layer-Aufrufen warten

Aufrufe an die Data Layer API, z. B. ein Aufruf mit der putDataItem Methode der DataClient Klasse, geben manchmal ein Task<ResultType> Objekt zurück. Sobald das Task-Objekt erstellt wurde, wird der Vorgang im Hintergrund in die Warteschlange gestellt. Wenn Sie danach nichts weiter tun, wird der Vorgang irgendwann im Hintergrund abgeschlossen.

In der Regel möchten Sie jedoch nach Abschluss des Vorgangs etwas mit dem Ergebnis tun. Mit dem Task-Objekt können Sie daher asynchron oder synchron auf den Ergebnisstatus warten.

Asynchrone Aufrufe

Wenn Ihr Code im Haupt-UI-Thread ausgeführt wird, senden Sie keine blockierenden Aufrufe an die Data Layer API und verwenden Sie eine Coroutine, um putDataItem aufzurufen:

private suspend fun Context.sendDataAsync(count: Int) {
    try {
        val putDataReq: PutDataRequest = PutDataMapRequest.create("/count").run {
            dataMap.putInt("count_key", count)
            asPutDataRequest()
        }
        val dataItem = Wearable.getDataClient(this).putDataItem(putDataReq).await()
        handleDataItem(dataItem)
    } catch (e: Exception) {
        handleDataItemError(e)
    } finally {
        handleTaskComplete()
    }
}

private fun handleDataItem(dataItem: DataItem) { }
private fun handleDataItemError(exception: Exception) { }
private fun handleTaskComplete() { }

Weitere Möglichkeiten, einschließlich der Verkettung der Ausführung verschiedener Aufgaben, finden Sie in der Task API.

Synchrone Aufrufe

Wenn Ihr Code in einem separaten Handler-Thread in einem Hintergrunddienst ausgeführt wird, z. B. in einem WearableListenerService, verwenden Sie runBlocking, um einen blockierenden Aufruf an putDataItem zu senden.

Hinweis:Rufen Sie diese Methode nicht im Hauptthread auf.

private fun Context.sendDataSync(count: Int) = runBlocking {
    val putDataReq = PutDataMapRequest.create("/count").run {
        dataMap.putInt("count_key", count)
        asPutDataRequest()
    }

    try {
        val result = Wearable.getDataClient(this@sendDataSync)
            .putDataItem(putDataReq)
            .await()
        // Logic for success
    } catch (e: Exception) {
        // Handle failure
    }
}

Auf Data Layer-Ereignisse warten

Da die Datenschicht Daten zwischen Smartphones und Wearables synchronisiert und sendet, müssen Sie in der Regel auf wichtige Ereignisse wie die Erstellung von Datenelementen und den Empfang von Nachrichten warten.

Dazu haben Sie zwei Möglichkeiten:

Bei beiden Optionen überschreiben Sie die Callback-Methoden für Datenereignisse für die Ereignisse, die Sie verarbeiten möchten.

Hinweis:Berücksichtigen Sie bei der Auswahl einer Listener-Implementierung die Akkunutzung Ihrer App. Ein WearableListenerService ist im Manifest der App registriert und kann die App starten, wenn sie noch nicht ausgeführt wird. Wenn Sie nur auf Ereignisse warten müssen, wenn Ihre App bereits ausgeführt wird, was bei interaktiven Anwendungen häufig der Fall ist, verwenden Sie keinen WearableListenerService. Registrieren Sie stattdessen einen Live-Listener. Verwenden Sie beispielsweise die addListener Methode der DataClient Klasse. Dadurch kann die Belastung des Systems und die Akkunutzung reduziert werden.

WearableListenerService verwenden

In der Regel erstellen Sie Instanzen von WearableListenerService sowohl in Ihren Wearable- als auch in Ihren Smartphone-Apps. Wenn Sie jedoch nicht an Datenereignissen in einer der Apps interessiert sind, müssen Sie den Dienst nicht in dieser App implementieren.

Sie können beispielsweise eine Smartphone-App haben, die Datenelementobjekte festlegt und abruft, und eine Wearable-App, die auf diese Aktualisierungen wartet, um die UI zu aktualisieren. Die Wearable-App aktualisiert keine der Datenelemente. Daher wartet die Smartphone-App nicht auf Datenereignisse von der Wearable-App.

Einige der Ereignisse, auf die Sie mit WearableListenerService warten können, sind:

  • onDataChanged(): Wenn ein Datenelementobjekt erstellt, gelöscht oder geändert wird, löst das System diesen Callback auf allen verbundenen Knoten aus.
  • onMessageReceived(): Eine von einem Knoten gesendete Nachricht löst diesen Callback auf dem Zielknoten aus.
  • onCapabilityChanged(): Wenn eine Funktion, die eine Instanz Ihrer App bewirbt, im Netzwerk verfügbar wird, löst dieses Ereignis diesen Callback aus. Wenn Sie nach einem Knoten in der Nähe suchen, können Sie die isNearby() Methode der im Callback bereitgestellten Knoten abfragen.

Sie können auch auf Ereignisse von ChannelClient.ChannelCallback warten, z. B. onChannelOpened().

Alle oben genannten Ereignisse werden in einem Hintergrundthread und nicht im Hauptthread ausgeführt.

So erstellen Sie einen WearableListenerService:

  1. Erstellen Sie eine Klasse, die WearableListenerService erweitert.
  2. Warten Sie auf die Ereignisse, die Sie interessieren, z. B. onDataChanged().
  3. Deklarieren Sie in Ihrem Android-Manifest einen Intent-Filter, um das System über Ihren WearableListenerService zu informieren. Mit dieser Deklaration kann das System Ihren Dienst nach Bedarf binden.

Im folgenden Beispiel wird gezeigt, wie Sie einen WearableListenerService implementieren:

class DataLayerListenerService : WearableListenerService() {

    override fun onDataChanged(dataEvents: DataEventBuffer) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onDataChanged: $dataEvents")
        }

        // Loop through the events and send a message
        // to the node that created the data item.
        dataEvents
            .map { it.dataItem.uri }
            .forEach { uri ->
                // Get the node ID from the host value of the URI.
                val nodeId: String = uri.host!!
                // Set the data of the message to be the bytes of the URI.
                val payload: ByteArray = uri.toString().toByteArray()

                // Send the RPC.
                Wearable.getMessageClient(this)
                    .sendMessage(
                        nodeId,
                        DATA_ITEM_RECEIVED_PATH,
                        payload
                    )
            }
    }
}

Im folgenden Abschnitt wird erläutert, wie Sie einen Intent-Filter mit diesem Listener verwenden.

Filter mit WearableListenerService verwenden

Ein Intent-Filter für das WearableListenerService-Beispiel im vorherigen Abschnitt könnte so aussehen:

<service
    android:name=".snippets.datalayer.DataLayerListenerService"
    android:exported="true"
    tools:ignore="ExportedService" >
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
        <data
            android:scheme="wear"
            android:host="*"
            android:path="/start-activity" />
    </intent-filter>
</service>

Der Aktionsfilter DATA_CHANGED informiert das System darüber, dass Ihre App an Ereignissen der Datenschicht interessiert ist.

In diesem Beispiel wartet die Smartwatch auf das Datenelement /start-activity und das Smartphone auf die Nachrichtenantwort /data-item-received (DATA_ITEM_RECEIVED_PATH).

Es gelten die Standardregeln für Android-Filter. Sie können mehrere Dienste pro Manifest, mehrere Intent-Filter pro Dienst, mehrere Aktionen pro Filter und mehrere Datenabschnitte pro Filter angeben. Filter können mit einem Platzhalterhost oder einem bestimmten Host übereinstimmen. Verwenden Sie host="*" für einen Platzhalterhost. Geben Sie host=<node_id> an, um einen bestimmten Host abzugleichen.

Sie können auch einen Literalpfad oder ein Pfadpräfix abgleichen. Dazu müssen Sie einen Platzhalter oder einen bestimmten Host angeben. Andernfalls ignoriert das System den von Ihnen angegebenen Pfad.

Weitere Informationen zu den von Wear OS unterstützten Filtertypen finden Sie in der API Referenzdokumentation für WearableListenerService.

Weitere Informationen zu Datenfiltern und Abgleichsregeln finden Sie in der API-Referenz dokumentation für das <data> Manifestelement.

Beachten Sie beim Abgleichen von Intent-Filtern zwei wichtige Regeln:

  • Wenn für den Intent-Filter kein Schema angegeben ist, ignoriert das System alle anderen URI-Attribute.
  • Wenn für den Filter kein Host angegeben ist, ignoriert das System alle Pfadattribute.

Live-Listener verwenden

Wenn Ihre App nur an Ereignissen der Datenschicht interessiert ist, wenn der Nutzer mit der App interagiert, ist möglicherweise kein Dienst erforderlich, der ständig ausgeführt wird, um jede Datenänderung zu verarbeiten. In diesem Fall können Sie in einer Aktivität auf Ereignisse warten.

Verwenden Sie für einen saubereren und sichereren Ansatz einen Lifecycle-Observer. Wenn Sie einen Lifecycle-Observer verwenden, verschieben Sie die Registrierungslogik aus dem LifecycleResumeEvent der Aktivität in eine separate, wiederverwendbare Klasse, die DefaultLifecycleObserver implementiert.

So bleibt Ihre Aktivität schlank und Sie vermeiden häufige Fehler wie das Vergessen, die Registrierung des Listeners aufzuheben.

1. Lifecycle-fähigen Listener erstellen

Diese Klasse umschließt den DataClient.OnDataChangedListener und verwaltet das eigene Abo automatisch basierend auf dem Lebenszyklus der Aktivität.

class WearDataLayerObserver(
    private val dataClient: DataClient,
    private val onDataReceived: (DataEventBuffer) -> Unit
) : DefaultLifecycleObserver, DataClient.OnDataChangedListener {

    // Implementation of the DataClient listener
    override fun onDataChanged(dataEvents: DataEventBuffer) {
        onDataReceived(dataEvents)
    }

    // Automatically register when the Activity starts
    override fun onResume(owner: LifecycleOwner) {
        dataClient.addListener(this)
    }

    // Automatically unregister when the Activity pauses
    override fun onPause(owner: LifecycleOwner) {
        dataClient.removeListener(this)
    }
}

2. Verwendung in Ihrer Aktivität

Jetzt muss Ihre Aktivität LifecycleResumeEvent oder onPause nicht mehr für die Wear API verwenden. Sie registrieren den Observer einmal in LaunchedEvent (oder onCreate).

class DataLayerLifecycleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val dataClient = Wearable.getDataClient(this)

        // Create the observer and link it to the activity's lifecycle
        val wearObserver = WearDataLayerObserver(dataClient) { dataEvents ->
            handleDataEvents(dataEvents)
        }

        lifecycle.addObserver(wearObserver)
    }

    private fun handleDataEvents(dataEvents: DataEventBuffer) {
        // ... filter and process events ...
    }
}

Vorteile:

  • Aktivität säubern:Sie entfernen Boilerplate-Code aus den Methoden des Aktivitätslebenszyklus.
  • Sicherheit:DefaultLifecycleObserver sorgt dafür, dass der Listener auch dann entfernt wird, wenn die Aktivität unerwartet beendet wird, und verhindert so Speicherlecks.
  • Wiederverwendbarkeit:Sie können diesen WearDataLayerObserver in jede Aktivität oder Composable einfügen, ohne die Registrierungslogik neu schreiben zu müssen.
  • Entkopplung:Die Logik für das Warten auf Ereignisse ist von der Logik für die Verarbeitung der Daten getrennt.

Filter mit Live-Listenern verwenden

Wie bereits erwähnt, können Sie Intent-Filter nicht nur für manifestbasierte WearableListenerService Objekte angeben, sondern auch, wenn Sie Intent-Filter beim Registrieren eines Live-Listeners über die Wearable API verwenden. Für API-basierte Live-Listener und manifestbasierte Listener gelten dieselben Regeln.

Ein häufiges Muster ist, einen Listener mit einem bestimmten Pfad oder Pfadpräfix zu registrieren mit collectAsStateWithLifecycle(). Wenn Sie Listener auf diese Weise implementieren, kann Ihre App Ereignisse selektiver empfangen, was das Design und die Effizienz verbessert.