Datenschichtereignisse für Wear verarbeiten

Wenn Sie die Data Layer API aufrufen, können Sie den Status des Aufrufs abrufen, sobald er abgeschlossen ist. Du kannst auch auf Datenereignisse warten, die durch Datenänderungen deiner App im gesamten Wear OS by Google-Netzwerk entstehen.

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

Auf Status der Datenschichtaufrufe warten

Aufrufe der Data Layer API, z. B. Aufrufe mit der Methode putDataItem der Klasse DataClient, 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 mehr unternehmen, wird der Vorgang im Hintergrund abgeschlossen.

Normalerweise möchten Sie jedoch nach Abschluss des Vorgangs etwas mit dem Ergebnis machen. Deshalb können Sie mit dem Task-Objekt entweder asynchron oder synchron auf den Ergebnisstatus warten.

Asynchrone Aufrufe

Wenn der Code im Hauptthread der Benutzeroberfläche ausgeführt wird, sollten Sie keine blockierenden Aufrufe an die Data Layer API ausführen. Führen Sie die Aufrufe asynchron aus, indem Sie dem Task-Objekt eine Callback-Methode hinzufügen, die nach Abschluss des Vorgangs ausgelöst wird:

Kotlin

// Using Kotlin function references
task.addOnSuccessListener(::handleDataItem)
task.addOnFailureListener(::handleDataItemError)
task.addOnCompleteListener(::handleTaskComplete)
...
fun handleDataItem(dataItem: DataItem) { ... }
fun handleDataItemError(exception: Exception) { ... }
fun handleTaskComplete(task: Task<DataItem>) { ... }

Java

// Using Java 8 Lambdas.
task.addOnSuccessListener(dataItem -> handleDataItem(dataItem));
task.addOnFailureListener(exception -> handleDataItemError(exception));
task.addOnCompleteListener(task -> handleTaskComplete(task));

Weitere Möglichkeiten, etwa das Verketten der Ausführung verschiedener Aufgaben, finden Sie in der Task API.

Synchrone Aufrufe

Wenn der Code in einem separaten Handler-Thread in einem Hintergrunddienst wie in einem WearableListenerService ausgeführt wird, können die Aufrufe blockiert werden. In diesem Fall können Sie Tasks.await() für das Task-Objekt aufrufen. Dieses wird blockiert, bis die Anfrage abgeschlossen ist und ein Result-Objekt zurückgibt. Dies wird im folgenden Beispiel dargestellt.

Hinweis:Achten Sie darauf, dass es während des Hauptthreads nicht aufgerufen wird.

Kotlin

try {
    Tasks.await(dataItemTask).apply {
        Log.d(TAG, "Data item set: $uri")
    }
}
catch (e: ExecutionException) { ... }
catch (e: InterruptedException) { ... }

Java

try {
    DataItem item = Tasks.await(dataItemTask);
    Log.d(TAG, "Data item set: " + item.getUri());
} catch (ExecutionException | InterruptedException e) {
  ...
}

Auf Datenschichtereignisse warten

Da die Datenschicht Daten zwischen den Handheld- und Wearable-Geräten synchronisiert und sendet, müssen Sie in der Regel auf wichtige Ereignisse wie das Erstellen von Datenelementen und das Empfangen von Nachrichten warten.

Sie haben zwei Möglichkeiten, auf Datenschichtereignisse zu warten:

Bei beiden Optionen überschreiben Sie die Callback-Methoden für Datenereignisse für die Ereignisse, die verarbeitet werden sollen.

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, sofern sie noch nicht ausgeführt wird. Wenn Sie nur auf Ereignisse warten müssen, wenn Ihre Anwendung bereits ausgeführt wird, was häufig bei interaktiven Anwendungen der Fall ist, sollten Sie kein WearableListenerService verwenden. Registrieren Sie stattdessen einen Live-Zuhörer. Verwenden Sie beispielsweise die Methode addListener der Klasse DataClient. Dies kann die Belastung des Systems und die Akkunutzung verringern.

WearableListenerService verwenden

Normalerweise erstellen Sie Instanzen von WearableListenerService sowohl in Wearable- als auch in Handheld-Apps. Wenn Sie jedoch nicht an Datenereignissen in einer der Anwendungen interessiert sind, müssen Sie den Dienst nicht in dieser Anwendung implementieren.

Sie können beispielsweise eine Handheld-App haben, die Datenelementobjekte festlegt und abruft, und eine Wearable-App, die auf diese Aktualisierungen wartet, um ihre UI zu aktualisieren. Die Wearable-App aktualisiert niemals Datenelemente. Daher wartet die Handheld-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 von einer Instanz Ihrer App beworbene Funktion 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 Methode isNearby() der Knoten aus dem Callback abfragen.

Sie können auch auf Ereignisse von ChannelClient.ChannelCallback wie onChannelOpened() warten.

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

So erstellen Sie ein WearableListenerService:

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

Das folgende Beispiel zeigt, wie ein einfaches WearableListenerService implementiert wird:

Kotlin

private const val TAG = "DataLayerSample"
private const val START_ACTIVITY_PATH = "/start-activity"
private const val DATA_ITEM_RECEIVED_PATH = "/data-item-received"

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

Java

public class DataLayerListenerService extends WearableListenerService {
    private static final String TAG = "DataLayerSample";
    private static final String START_ACTIVITY_PATH = "/start-activity";
    private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        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.
        for (DataEvent event : dataEvents) {
            Uri uri = event.getDataItem().getUri();

            // Get the node ID from the host value of the URI.
            String nodeId = uri.getHost();
            // Set the data of the message to be the bytes of the URI.
            byte[] payload = uri.toString().getBytes();

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

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

Filter mit WearableListenerService verwenden

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

<service android:name=".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>

In diesem Filter ersetzt die Aktion DATA_CHANGED die zuvor empfohlene BIND_LISTENER-Aktion, damit deine App nur bei bestimmten Ereignissen aktiviert oder gestartet wird. Diese Änderung verbessert die Systemeffizienz und verringert den Akkuverbrauch und andere mit Ihrer App verbundene Kosten. In diesem Beispiel wartet die Smartwatch auf das Datenelement /start-activity und das Smartphone auf die Nachrichtenantwort /data-item-received.

Es gelten die Standardregeln zum Abgleich von Android-Filtern. Sie können mehrere Dienste pro Manifest, mehrere Intent-Filter pro Dienst, mehrere Aktionen pro Filter und mehrere Daten-Stanzas pro Filter angeben. Filter können nach einem Platzhalterhost oder einem bestimmten Host abgeglichen werden. Für den Abgleich mit einem Platzhalterhost verwenden Sie host="*". Geben Sie host=<node_id> für die Übereinstimmung mit einem bestimmten Host an.

Sie können auch mit einem literalen Pfad oder einem 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 findest du in der API-Referenzdokumentation für WearableListenerService.

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

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

  • Wenn kein Schema für den Intent-Filter 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 für Ihre Anwendung Ereignisse auf Datenebene nur relevant sind, wenn der Nutzer mit der Anwendung interagiert, ist möglicherweise kein Dienst mit langer Ausführungszeit erforderlich, um jede Datenänderung zu verarbeiten. In diesem Fall können Sie auf Ereignisse in einer Aktivität warten, indem Sie eine oder mehrere der folgenden Schnittstellen implementieren:

So erstellen Sie eine Aktivität, die Datenereignisse überwacht:

  1. Implementieren Sie die gewünschten Schnittstellen.
  2. Rufe in der Methode onCreate() oder onResume() Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener() oder ChannelClient.registerChannelCallback() auf, um Google Play-Dienste darüber zu informieren, dass deine Aktivität auf Datenschichtereignisse achten möchte.
  3. Heben Sie in onStop() oder onPause() die Registrierung aller Listener mit DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener() oder ChannelClient.unregisterChannelCallback() auf.
  4. Wenn eine Aktivität nur an Ereignissen mit einem bestimmten Pfadpräfix interessiert ist, können Sie einen Listener mit einem geeigneten Präfixfilter hinzufügen, um nur Daten zu empfangen, die für den aktuellen Anwendungsstatus relevant sind.
  5. Implementieren Sie je nach den implementierten Schnittstellen onDataChanged(), onMessageReceived(), onCapabilityChanged() oder Methoden aus ChannelClient.ChannelCallback. Diese Methoden werden im Hauptthread aufgerufen. Sie können aber auch mit WearableOptions einen benutzerdefinierten Looper angeben.

Hier ist ein Beispiel, in dem DataClient.OnDataChangedListener implementiert wird:

Kotlin

class MainActivity : Activity(), DataClient.OnDataChangedListener {

    public override fun onResume() {
        Wearable.getDataClient(this).addListener(this)
    }

    override fun onPause() {
        Wearable.getDataClient(this).removeListener(this)
    }

    override fun onDataChanged(dataEvents: DataEventBuffer) {
        dataEvents.forEach { event ->
            if (event.type == DataEvent.TYPE_DELETED) {
                Log.d(TAG, "DataItem deleted: " + event.dataItem.uri)
            } else if (event.type == DataEvent.TYPE_CHANGED) {
                Log.d(TAG, "DataItem changed: " + event.dataItem.uri)
            }
        }
    }
}

Java

public class MainActivity extends Activity implements DataClient.OnDataChangedListener {

    @Override
    public void onResume() {
        Wearable.getDataClient(this).addListener(this);
    }

    @Override
    protected void onPause() {
        Wearable.getDataClient(this).removeListener(this);
    }

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        for (DataEvent event : dataEvents) {
            if (event.getType() == DataEvent.TYPE_DELETED) {
                Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
                Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
            }
        }
    }
}

Filter mit Live-Listenern verwenden

Wie bereits erwähnt, können Sie Intent-Filter für die Registrierung eines Live-Listeners über die Wearable API verwenden, genau wie Sie Intent-Filter für Manifest-basierte WearableListenerService-Objekte angeben können. Für API-basierte Live-Listener und Manifest-basierte Listener gelten dieselben Regeln.

Ein gängiges Muster besteht darin, einen Listener mit einem bestimmten Pfad oder Pfadpräfix in der Methode onResume() einer Aktivität zu registrieren und den Listener dann aus der Methode onPause() der Aktivität zu entfernen. Wenn Sie Listener auf diese Weise implementieren, kann Ihre Anwendung Ereignisse selektiver empfangen, wodurch das Design und die Effizienz verbessert werden.