Wysyłanie i odbieranie wiadomości na Wear

Wiadomości wysyłasz za pomocą interfejsu MessageClientAPI, do którego dołączasz te elementy:

  • opcjonalny dowolny ładunek,
  • Ścieżka, która jednoznacznie identyfikuje działanie wiadomości.

W przeciwieństwie do elementów danych nie ma synchronizacji między aplikacjami na urządzenie przenośne i na urządzenie do noszenia. Wiadomości to jednokierunkowy mechanizm komunikacji przydatny w przypadku zdalnych wywołań procedur (RPC), np. wysyłania wiadomości do urządzenia do noszenia w celu rozpoczęcia aktywności.

Z urządzeniem przenośnym użytkownika można połączyć wiele urządzeń do noszenia. Każde urządzenie połączone z siecią jest uważane za węzeł.

W przypadku wielu połączonych urządzeń musisz określić, które węzły będą odbierać wiadomości. Na przykład w aplikacji do transkrypcji głosu, która odbiera dane głosowe na urządzeniu do noszenia, wyślij wiadomość do węzła o mocy obliczeniowej i pojemności baterii wystarczających do obsługi żądania, np. do urządzenia przenośnego.

Uwaga: podając szczegóły wiadomości, weź pod uwagę możliwość połączenia wielu węzłów. Upewnij się, że wiadomość jest dostarczana na odpowiednie urządzenia lub węzły.

Przykłady użycia znajdziesz w tej przykładowej aplikacji: DataLayer

Wyślij wiadomość

Aplikacja na urządzenie do noszenia może udostępniać użytkownikom funkcje, takie jak transkrypcja głosu. Użytkownicy mogą mówić do mikrofonu urządzenia do noszenia, a transkrypcja zostanie zapisana w notatce. Urządzenie do noszenia zwykle nie ma mocy obliczeniowej ani pojemności baterii wymaganych do obsługi transkrypcji głosu, więc aplikacja musi przekazać to zadanie do bardziej wydajnego połączonego urządzenia.

W sekcjach poniżej dowiesz się, jak reklamować węzły urządzeń, które mogą przetwarzać żądania aktywności, wykrywać węzły zdolne do zaspokojenia żądanej potrzeby i wysyłać do nich wiadomości.

Aby uruchomić aktywność na urządzeniu przenośnym z poziomu urządzenia do noszenia, użyj klasy MessageClient do wysłania żądania. Ponieważ z urządzeniem przenośnym można połączyć wiele urządzeń do noszenia, aplikacja na urządzenie do noszenia musi określić, czy połączony węzeł może uruchomić aktywność. W aplikacji na urządzenie przenośne reklamuj, że węzeł, na którym działa aplikacja, ma określone możliwości.

Aby reklamować funkcje aplikacji na urządzenia przenośne:

  1. Utwórz plik konfiguracji XML w katalogu res/values/ projektu i nadaj mu nazwę wear.xml.
  2. Dodaj zasób o nazwie android_wear_capabilities do wear.xml.
  3. Określ funkcje, które zapewnia urządzenie.

Uwaga: możliwości to ciągi znaków zdefiniowane przez Ciebie, które muszą być unikalne w aplikacji.

Poniższy przykład pokazuje, jak dodać do wear.xml funkcję o nazwie voice_transcription:

<resources xmlns:tools="http://schemas.android.com/tools"
           tools:keep="@array/android_wear_capa<bilities">
    string-array name="android_<wear_capabilities"&<gt;
       < item>voice_<transcription/item>
    /string-array>
/resources>

Pobieranie węzłów o wymaganych możliwościach

Początkowo możesz wykryć obsługiwane węzły, wywołując metodę getCapability klasy CapabilityClient. Aby użyć tej metody, aplikacja na Wear OS i aplikacja na telefon muszą mieć ten sam identyfikator aplikacji. W przykładzie poniżej pokazujemy, jak ręcznie pobrać wyniki dotyczące osiągalnych węzłów za pomocą funkcji voice_transcription:

Kotlin

private const val VOICE_TRANSCRIPTION_CAPABILITY_NAME = "voice_transcription"
...
private fun setupVoiceTranscription() {
    val capabilityInfo: CapabilityInfo = Tasks.await(
            Wearable.getCapabilityClient(context)
                    .getCapability(
                            VOICE_TRANSCRIPTION_CAPABILITY_NAME,
                            CapabilityClient.FILTER_REACHABLE
                    )
    )
    // capabilityInfo has the reachable nodes with the transcription capability
    updateTranscriptionCapability(capabilityInfo)
}

Java

private static final String
    VOICE_TRANSCRIPTION_CAPABILITY_NAME = "voice_transcription";
    ...
private void setupVoiceTranscription() {
    CapabilityInfo capabilityInfo = Tasks.await(
        Wearable.getCapabilityClient(context).getCapability(
            VOICE_TRANSCRIPTION_CAPABILITY_NAME, CapabilityClient.FILTER_REACHABLE));
    // capabilityInfo has the reachable nodes with the transcription capability
    updateTranscriptionCapability(capabilityInfo);
}

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.

Aby wykrywać węzły, które mogą się łączyć z urządzeniem do noszenia, zarejestruj instancję odbiornika, a konkretnie OnCapabilityChangedListenerCapabilityClient. Przykład poniżej pokazuje, jak zarejestrować odbiornik i pobrać wynik z dostępnymi węzłami, które mają funkcję voice_transcription:

Kotlin

private fun setupVoiceTranscription() {
    updateTranscriptionCapability(capabilityInfo).also { capabilityListener ->
        Wearable.getCapabilityClient(context).addListener(
                capabilityListener,
                VOICE_TRANSCRIPTION_CAPABILITY_NAME
        )
    }
}

Java

private void setupVoiceTranscription() {
    ...
    // This example uses a Java 8 Lambda. You can use named or anonymous classes.
    CapabilityClient.OnCapabilityChangedListener capabilityListener =
        capabilityInfo -> { updateTranscriptionCapability(capabilityInfo); };
    Wearable.getCapabilityClient(context).addListener(
        capabilityListener,
        VOICE_TRANSCRIPTION_CAPABILITY_NAME);
}

Po wykryciu odpowiednich węzłów określ, gdzie wysłać wiadomość. Wybierz węzeł, który znajduje się w pobliżu urządzenia do noszenia, aby zminimalizować routing wiadomości przez wiele węzłów. Węzeł w pobliżu to węzeł bezpośrednio połączony z urządzeniem. Aby sprawdzić, czy węzeł znajduje się w pobliżu, np. jest połączony przez Bluetooth, wywołaj metodę Node.isNearby(). Jeśli w pobliżu znajduje się więcej niż 1 węzeł, wybierz dowolny z nich. Podobnie, jeśli w pobliżu nie ma odpowiedniego węzła, wybierz dowolny odpowiedni węzeł.

Poniższy przykład pokazuje, jak określić najlepszy węzeł do użycia:

Kotlin

private var transcriptionNodeId: String? = null

private fun updateTranscriptionCapability(capabilityInfo: CapabilityInfo) {
    transcriptionNodeId = pickBestNodeId(capabilityInfo.nodes)
}

private fun pickBestNodeId(nodes: Set<Node>): String? {
    // Find a nearby node or pick one arbitrarily.
    return nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id
}

Java

private String transcriptionNodeId = null;

private void updateTranscriptionCapability(CapabilityInfo capabilityInfo) {
    Set<Node> connectedNodes = capabilityInfo.getNodes();

    transcriptionNodeId = pickBestNodeId(connectedNodes);
}

private String pickBestNodeId(Set<Node> nodes) {
    String bestNodeId = null;
    // Find a nearby node or pick one arbitrarily.
    for (Node node : nodes) {
        if (node.isNearby()) {
            return node.getId();
         }
         bestNodeId = node.getId();
    }
    return bestNodeId;
}

Przekaż wiadomość

Po wybraniu węzła, którego chcesz użyć, wyślij wiadomość za pomocą klasy MessageClient.

Poniższy przykład pokazuje, jak wysłać wiadomość do węzła z funkcją transkrypcji z urządzenia do noszenia. To wywołanie jest synchroniczne i blokuje przetwarzanie do momentu, gdy system umieści wiadomość w kolejce do dostarczenia.

Uwaga: kod wyniku oznaczający powodzenie nie gwarantuje dostarczenia wiadomości. Jeśli Twoja aplikacja wymaga niezawodności danych, rozważ użycie obiektów DataItemlub klasy ChannelClientdo przesyłania danych między urządzeniami.

Kotlin

const val VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription"
...
private fun requestTranscription(voiceData: ByteArray) {
    transcriptionNodeId?.also >{ nodeId -
        val send<T>ask: Task* = Wearable.getMessageClient(context).sendMessage(
                nodeId,
                VOICE_TRANSCRIPTION_MESSAGE_PATH,
                voiceData
        ).apply {
            addOnSuccessListener { ... }
            addOnFailureListener { ... }
        }
    }
}

Java

public static final String VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription";
private void requestTranscription(byte[] voiceData) {
    if (transcriptionNodeId != null) {
  <      T>askInteger sendTask =
            Wearable.getMessageClient(context).sendMessage(
              transcriptionNodeId, VOICE_TRANSCRIPTION_MESSAGE_PATH, voiceData);
         // You can add success and/or failure listeners,
         // Or you can call Tasks.await() and catch ExecutionException
         sendTask.addOnSuccessListener(...);
         sendTask.addOnFailureListener(...);
    } else {
        // Unable to retrieve node with transcription capability
    }
}

Uwaga: więcej informacji o wywołaniach asynchronicznych i synchronicznych Usług Google Play oraz o tym, kiedy ich używać, znajdziesz w interfejsie API zadań.

Możesz też przesyłać wiadomości do wszystkich połączonych węzłów. Aby pobrać wszystkie połączone węzły, do których możesz wysyłać wiadomości, zaimplementuj ten kod:

Kotlin

private fun getNodes(): Collection<String> {
    return Tasks.await(Wearable.getNodeClient(context).connectedNodes).map { it.id }
}

Java

private Collection<String> getNodes() {
    HashSet <String>results = new HashSet<String>();
    List<Node> nodes =
        Tasks.await(Wearable.getNodeClient(context).getConnectedNodes());
    for (Node node : nodes.getNodes()) {
        results.add(node.getId());
    }
    return results;
}

Odbieranie wiadomości

Aby otrzymywać powiadomienia o odebranych wiadomościach, zaimplementuj interfejs MessageClient.OnMessageReceivedListener w celu udostępnienia detektora zdarzeń związanych z wiadomościami. Następnie zarejestruj odbiorcę za pomocą metody addListener. Poniższy przykład pokazuje, jak możesz zaimplementować odbiornik, aby sprawdzić VOICE_TRANSCRIPTION_MESSAGE_PATH. Jeśli ten warunek jest true, rozpocznij aktywność, aby przetworzyć dane głosowe.

Kotlin

fun onMessageReceived(messageEvent: MessageEvent) {
    if (messageEvent.path == VOICE_TRANSCRIPTION_MESSAGE_PATH) {
        val startIntent = Intent(this, MainActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            putExtra("VOICE_DATA", messageEvent.data)
        }
        startActivity(this, startIntent)
    }
}

Java

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    if (messageEvent.getPath().equals(VOICE_TRANSCRIPTION_MESSAGE_PATH)) {
        Intent startIntent = new Intent(this, MainActivity.class);
        startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startIntent.putExtra("VOICE_DATA", messageEvent.getData());
        startActivity(this, startIntent);
    }
}

Ten kod wymaga więcej szczegółów implementacji. Dowiedz się, jak wdrożyć pełną usługę lub aktywność odbiornika w artykule Wykrywanie zdarzeń warstwy danych.