Obsługa zdarzeń warstwy danych na Wear

Gdy wywołujesz interfejs Data Layer API, możesz otrzymać stan wywołania po jego zakończeniu. Możesz też nasłuchiwać zdarzeń danych wynikających ze zmian w danych wprowadzonych przez aplikację w dowolnym miejscu sieci Wear OS by Google.

Przykład skutecznej pracy z interfejsem Data Layer API znajdziesz w aplikacji Android DataLayer Sample.

Poczekaj na stan wywołań warstwy danych

Wywołania interfejsu API warstwy danych, np. wywołanie metody putDataItem klasy DataClient, czasami zwracają obiekt Task<ResultType>. Gdy tylko zostanie utworzony obiekt Task, operacja zostanie dodana do kolejki w tle. Jeśli po wykonaniu tej czynności nie podejmiesz żadnych działań, operacja zakończy się po cichu.

Zwykle jednak chcesz coś zrobić z wynikiem po zakończeniu operacji, dlatego obiekt Task pozwala czekać na stan wyniku – asynchronicznie lub synchronicznie.

Wywołania asynchroniczne

Jeśli Twój kod działa w głównym wątku interfejsu użytkownika, nie wysyłaj blokujących wywołań do interfejsu Data Layer API. Uruchamiaj wywołania asynchronicznie, dodając do obiektu Task metodę wywołania zwrotnego, która uruchamia się po zakończeniu operacji:

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

Informacje o innych możliwościach, w tym o łańcuchu wykonywania różnych zadań, znajdziesz w sekcji Task API.

Połączenia synchroniczne

Jeśli Twój kod działa w osobnym wątku modułu obsługi w usłudze w tle, np. w WearableListenerService, wywołania można blokować. W tym przypadku możesz wywołać w obiekcie Task metodę Tasks.await(), która blokuje proces, dopóki żądanie nie zostanie ukończone i zwróci obiekt Result. Widać to w przykładzie poniżej.

Uwaga: pamiętaj, aby nie wywoływać tej funkcji w wątku głównym.

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

Nasłuchiwanie zdarzeń warstwy danych

Warstwa danych synchronizuje i wysyła dane między urządzeniami przenośnymi i urządzeniami do noszenia, więc zwykle musisz wychwytywać ważne zdarzenia, takie jak tworzenie elementów danych i odbieranie wiadomości.

Jeśli chcesz nasłuchiwać zdarzeń warstwy danych, możesz to zrobić na 2 sposoby:

W przypadku obu tych opcji zastępujesz metody wywołania zwrotnego zdarzeń danych w przypadku zdarzeń, które chcesz obsługiwać.

Uwaga: wybierając implementację odbiornika, weź pod uwagę zużycie baterii przez aplikację. WearableListenerService jest zarejestrowany w pliku manifestu aplikacji i może uruchomić aplikację, jeśli nie jest jeszcze uruchomiona. Jeśli chcesz nasłuchiwać zdarzeń tylko wtedy, gdy Twoja aplikacja jest już uruchomiona, co często ma miejsce w przypadku aplikacji interaktywnych, nie używaj WearableListenerService. Zamiast tego zarejestruj słuchacza na żywo. Użyj na przykład metody addListener klasy DataClient. Może to zmniejszyć obciążenie systemu i zmniejszyć wykorzystanie baterii.

Używanie usługi WearableListenerService

Zwykle instancje WearableListenerService tworzysz zarówno w aplikacjach na urządzenia do noszenia, jak i na urządzeniach mobilnych. Jeśli jednak nie interesują Cię zdarzenia danych w jednej z aplikacji, nie musisz w niej implementować tej usługi.

Możesz na przykład używać aplikacji przenośnej, która ustawia i pobiera obiekty elementów danych, oraz aplikację do noszenia, która nasłuchuje tych aktualizacji, aby zaktualizować swój interfejs. Aplikacja na urządzenia do noszenia nigdy nie aktualizuje żadnych elementów danych, więc nie nasłuchuje żadnych zdarzeń danych z aplikacji.

Oto niektóre zdarzenia, których możesz nasłuchiwać za pomocą WearableListenerService:

  • onDataChanged(): za każdym razem, gdy obiekt elementu danych zostanie utworzony, usunięty lub zmieniony, system aktywuje to wywołanie zwrotne we wszystkich połączonych węzłach.
  • onMessageReceived(): wiadomość wysłana z węzła aktywuje to wywołanie zwrotne w węźle docelowym.
  • onCapabilityChanged(): gdy możliwość reklamowana przez daną instancję aplikacji staje się dostępna w sieci, zdarzenie to wywołuje wywołanie zwrotne. Jeśli szukasz węzła w pobliżu, możesz przesłać zapytanie do metody isNearby() węzłów podanych w wywołaniu zwrotnym.

Możesz też nasłuchiwać zdarzeń z właściwości ChannelClient.ChannelCallback, takich jak onChannelOpened().

Wszystkie poprzednie zdarzenia są wykonywane w wątku w tle, a nie w wątku głównym.

Aby utworzyć WearableListenerService, wykonaj te czynności:

  1. Utwórz zajęcia z rozszerzeniem WearableListenerService.
  2. Słuchaj interesujących Cię zdarzeń, np. onDataChanged().
  3. Zadeklaruj w pliku manifestu Androida filtr intencji, aby powiadomić system o tym działaniu: WearableListenerService. Ta deklaracja umożliwia systemowi powiązanie usługi w razie potrzeby.

Ten przykład pokazuje, jak wdrożyć prosty WearableListenerService:

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

Z sekcji poniżej dowiesz się, jak używać filtra intencji z tym detektorem.

Używanie filtrów z WearableListenerService

Filtr intencji w przykładzie WearableListenerService pokazanym w poprzedniej sekcji może wyglądać tak:

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

W tym filtrze działanie DATA_CHANGED zastępuje wcześniej zalecane działanie BIND_LISTENER, tak aby tylko określone zdarzenia wybudzały lub uruchamiały Twoją aplikację. Ta zmiana zwiększa wydajność systemu oraz zmniejsza zużycie baterii i inne nakłady pracy związane z aplikacją. W tym przykładzie zegarek nasłuchuje elementu danych /start-activity, a telefon nasłuchuje odpowiedzi na wiadomość /data-item-received.

Obowiązują standardowe reguły dopasowania filtrów Androida. Możesz określić wiele usług w pliku manifestu, wiele filtrów intencji na usługę, wiele działań dla każdego filtra oraz wiele stanowisk w danych dla każdego filtra. Filtry mogą pasować do hosta z symbolem wieloznacznym lub do konkretnego hosta. Aby dopasować do hosta z symbolem wieloznacznym, użyj host="*". Aby dopasować do konkretnego hosta, wpisz host=<node_id>.

Możesz też dopasować ścieżkę literału lub prefiks ścieżki. Aby to zrobić, musisz podać symbol wieloznaczny lub konkretnego hosta. W przeciwnym razie system zignoruje podaną ścieżkę.

Więcej informacji o typach filtrów obsługiwanych przez Wear OS znajdziesz w dokumentacji interfejsu API WearableListenerService.

Więcej informacji o filtrach danych i regułach dopasowania znajdziesz w dokumentacji interfejsu API elementu manifestu <data>.

Dopasowując filtry intencji, pamiętaj o 2 ważnych regułach:

  • Jeśli w filtrze intencji nie określono schematu, system ignoruje wszystkie pozostałe atrybuty URI.
  • Jeśli w filtrze nie określisz hosta, system ignoruje wszystkie atrybuty ścieżek.

Korzystanie z słuchacza na żywo

Jeśli Twoja aplikacja interesuje tylko zdarzenia warstwy danych, gdy użytkownik wchodzi z nią w interakcję, być może nie będzie potrzebowała długotrwałej usługi do obsługi każdej zmiany danych. W takim przypadku możesz nasłuchiwać zdarzeń w aktywności, implementując co najmniej jeden z tych interfejsów:

Aby utworzyć działanie wykrywające zdarzenia dotyczące danych:

  1. Zaimplementuj odpowiednie interfejsy.
  2. W metodzie onCreate() lub onResume() wywołaj Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener() lub ChannelClient.registerChannelCallback(), aby powiadomić Usługi Google Play, że Twoja aktywność chce nasłuchiwać zdarzeń warstwy danych.
  3. W onStop() lub onPause() wyrejestruj wszystkich słuchaczy z DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener() lub ChannelClient.unregisterChannelCallback().
  4. Jeśli aktywność interesuje tylko zdarzenia z określonym prefiksem ścieżki, możesz dodać odbiornik z odpowiednim filtrem prefiksu, aby odbierać tylko dane związane z bieżącym stanem aplikacji.
  5. Zaimplementuj onDataChanged(), onMessageReceived(), onCapabilityChanged() lub metody z ChannelClient.ChannelCallback w zależności od zaimplementowanego interfejsu. Te metody są wywoływane w wątku głównym lub możesz określić niestandardowy obiekt Looper za pomocą metody WearableOptions.

Oto przykład zastosowania funkcji DataClient.OnDataChangedListener:

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

Korzystanie z filtrów w przypadku słuchaczy na żywo

Jak już wspomnieliśmy, tak jak możesz określać filtry intencji w przypadku obiektów WearableListenerService opartych na pliku manifestu, możesz używać filtrów intencji podczas rejestrowania detektora na żywo za pomocą Wearable API. Te same reguły dotyczą zarówno detektorów transmisji na żywo używających interfejsów API, jak i detektorów opartych na pliku manifestu.

Typowym wzorcem jest zarejestrowanie detektora za pomocą określonej ścieżki lub prefiksu ścieżki w metodzie onResume() działania, a następnie usunięcie detektora w metodzie onPause() aktywności. Zastosowanie detektorów w taki sposób umożliwi aplikacji bardziej selektywne odbieranie zdarzeń, co poprawi jej wygląd i wydajność.