Obsługa zdarzeń warstwy danych na Wear

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

Przykład skutecznego korzystania z interfejsu Data Layer API znajdziesz w aplikacji Android DataLayer Sample.

Oczekiwanie na stan wywołań warstwy danych

Wywołania interfejsu Data Layer API, np. wywołanie za pomocą metody putDataItem klasy DataClient, czasami zwracają obiekt Task<ResultType>. Gdy tylko obiekt Task zostanie utworzony, operacja zostanie umieszczona w kolejce w tle. Jeśli nie podejmiesz żadnych dalszych działań, operacja zostanie ostatecznie wykonana w trybie cichym.

Zwykle jednak po zakończeniu operacji chcesz coś zrobić z wynikiem, więc obiekt Task umożliwia oczekiwanie na stan wyniku asynchronicznie lub synchronicznie.

Połączenia asynchroniczne

Jeśli kod jest uruchamiany w głównym wątku interfejsu, nie wykonuj blokujących wywołań interfejsu Data Layer API. Uruchom wywołania asynchronicznie, dodając do obiektu Task metodę wywołania zwrotnego, która jest wywoływana 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));

Więcej możliwości, w tym łączenie wykonywania różnych zadań, znajdziesz w  interfejsie API zadań.

Połączenia synchroniczne

Jeśli kod jest uruchamiany w osobnym wątku obsługi w usłudze działającej w tle, np. w  WearableListenerService, blokowanie wywołań jest dopuszczalne. W takim przypadku możesz wywołać funkcję Tasks.await() na obiekcie Task, która blokuje działanie programu do czasu zakończenia żądania i zwraca obiekt Result. Pokazuje to poniższy przykład.

Uwaga: nie wywołuj tej funkcji w głównym wątku.

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 a urządzeniami do noszenia, dlatego zwykle musisz nasłuchiwać ważnych zdarzeń, takich jak tworzenie elementów danych i odbieranie wiadomości.

Aby nasłuchiwać zdarzeń warstwy danych, masz 2 możliwości:

W przypadku obu tych opcji zastępujesz metody wywołania zwrotnego zdarzenia danych dla zdarzeń, którymi chcesz się zajmować.

Uwaga: podczas wybierania implementacji odbiornika weź pod uwagę zużycie baterii przez aplikację. W pliku manifestu aplikacji zarejestrowany jest WearableListenerService, który może uruchomić aplikację, jeśli nie jest ona jeszcze uruchomiona. Jeśli musisz nasłuchiwać zdarzeń tylko wtedy, gdy aplikacja jest już uruchomiona (co często ma miejsce w przypadku aplikacji interaktywnych), nie używaj WearableListenerService. Zamiast tego zarejestruj detektor zdarzeń na żywo. Na przykład użyj metody addListener klasy DataClient. Może to zmniejszyć obciążenie systemu i zużycie baterii.

Używanie WearableListenerService

Zwykle tworzysz instancje WearableListenerService zarówno w aplikacjach na urządzenia przenośne, jak i na urządzenia do noszenia. Jeśli jednak nie interesują Cię zdarzenia związane z danymi w jednej z aplikacji, nie musisz wdrażać w niej tej usługi.

Możesz na przykład mieć aplikację na urządzenie przenośne, która ustawia i pobiera obiekty elementów danych, oraz aplikację na urządzenie do noszenia, która nasłuchuje tych aktualizacji, aby aktualizować interfejs. Aplikacja na urządzenie do noszenia nigdy nie aktualizuje żadnych elementów danych, więc aplikacja na urządzenie przenośne nie nasłuchuje żadnych zdarzeń danych z aplikacji na urządzenie do noszenia.

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

  • onDataChanged(): 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 wywołuje to wywołanie zwrotne w węźle docelowym.
  • onCapabilityChanged(): gdy w sieci stanie się dostępna funkcja, którą reklamuje instancja Twojej aplikacji, to zdarzenie wywołuje to wywołanie zwrotne. Jeśli szukasz węzła w pobliżu, możesz wysłać zapytanie do metody isNearby() węzłów podanych w wywołaniu zwrotnym.

Możesz też nasłuchiwać zdarzeń z  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 klasę, która rozszerza WearableListenerService.
  2. Nasłuchuj wydarzeń, które Cię interesują, np. onDataChanged().
  3. Zadeklaruj filtr intencji w pliku manifestu Androida, aby powiadomić system o swojej WearableListenerService. Ta deklaracja umożliwia systemowi powiązanie usługi w razie potrzeby.

Poniższy przykład pokazuje, jak wdrożyć prosty element 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);
        }
    }
}

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

Korzystanie z filtrów w WearableListenerService

Filtr intencji dla przykładu WearableListenerService z 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, dzięki czemu tylko określone zdarzenia wybudzają lub uruchamiają aplikację. Ta zmiana zwiększa wydajność systemu oraz zmniejsza zużycie baterii i inne obciążenie 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 dopasowywania filtrów na Androidzie. W jednym pliku manifestu możesz określić wiele usług, w jednym filtrze intencji – wiele działań, a w jednym filtrze – wiele sekcji danych. Filtry mogą pasować do hosta z symbolem wieloznacznym lub do konkretnego hosta. Aby dopasować symbol wieloznaczny hosta, użyj host="*". Aby dopasować do konkretnego hosta, podaj host=<node_id>.

Możesz też dopasować dosłowną ścieżkę lub prefiks ścieżki. Aby to zrobić, musisz określić 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 referencyjnej interfejsu API WearableListenerService.

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

Podczas dopasowywania filtrów intencji pamiętaj o 2 ważnych zasadach:

  • Jeśli w filtrze intencji nie określono schematu, system ignoruje wszystkie inne atrybuty URI.
  • Jeśli w filtrze nie podasz hosta, system zignoruje wszystkie atrybuty ścieżki.

Korzystanie z funkcji słuchania na żywo

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

Aby utworzyć aktywność, która nasłuchuje zdarzeń danych:

  1. Zaimplementuj wybrane 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ść jest zainteresowana nasłuchiwaniem zdarzeń warstwy danych.
  3. W przypadku onStop() lub onPause() wyrejestruj wszystkie detektory za pomocą DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener() lub ChannelClient.unregisterChannelCallback().
  4. Jeśli aktywność jest zainteresowana tylko zdarzeniami z określonym prefiksem ścieżki, możesz dodać odbiornik z odpowiednim filtrem prefiksu, aby otrzymywać tylko dane istotne dla bieżącego stanu aplikacji.
  5. Wdróż metody onDataChanged(), onMessageReceived(), onCapabilityChanged() lub ChannelClient.ChannelCallback w zależności od zaimplementowanych interfejsów. Te metody są wywoływane w głównym wątku. Możesz też określić niestandardowy Looper za pomocą WearableOptions.

Oto przykład implementacji właściwości 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());
            }
        }
    }
}

Ostrzeżenie: zanim użyjesz interfejsu Wearable Data Layer API, sprawdź, czy jest on dostępny na urządzeniu. W przeciwnym razie wystąpi wyjątek. Użyj klasy GoogleApiAvailability, tak jak w przypadku klasy Horologist.

Używanie filtrów w przypadku słuchaczy na żywo

Jak wspomnieliśmy wcześniej, tak jak możesz określać filtry intencji dla obiektów opartych na pliku manifestu, możesz też używać filtrów intencji podczas rejestrowania aktywnego odbiornika za pomocą Wearable API.WearableListenerService Te same zasady obowiązują zarówno w przypadku słuchaczy korzystających z interfejsu API, jak i słuchaczy korzystających z pliku manifestu.

Często stosowanym wzorcem jest rejestrowanie detektora z określoną ścieżką lub prefiksem ścieżki w metodzie onResume() działania, a następnie usuwanie detektora w metodzie onPause() działania. Wdrożenie odbiorników w ten sposób pozwala aplikacji bardziej selektywnie odbierać zdarzenia, co poprawia jej projekt i wydajność.