Cómo enviar y recibir mensajes en Wear

Envías mensajes utilizando la API de MessageClient y adjuntas los siguientes elementos al mensaje:

  • Una carga útil arbitraria (opcional)
  • Una ruta de acceso que identifica exclusivamente la acción del mensaje.

A diferencia de lo que ocurre en el caso de los elementos de datos, no se produce ninguna sincronización entre las apps para dispositivos portátiles y para wearables. Los mensajes son un mecanismo de comunicación unidireccional apropiado para las llamadas de procedimientos remotos (RPC), como el envío de un mensaje al wearable para iniciar una actividad.

Se pueden conectar varios wearables al dispositivo portátil de un usuario. Cada dispositivo conectado en la red se considera un nodo. Al tener varios dispositivos conectados, debes considerar qué nodos reciben los mensajes. Por ejemplo, en una app de transcripción de voz que recibe datos de voz en el wearable, debes enviar el mensaje a un nodo con la potencia de procesamiento y la capacidad de batería para manejar la solicitud, como un dispositivo portátil.

Nota: Con las versiones de los Servicios de Google Play anteriores a 7.3.0, solo se podía conectar un wearable a un dispositivo portátil a la vez. Es posible que debas actualizar tu código existente para que se tenga en cuenta la función de varios nodos conectados. Si no implementas los cambios, tus mensajes podrían no entregarse a los dispositivos que deseas.

Cómo enviar un mensaje

Una app para wearable puede proporcionar funciones como la transcripción de voz a los usuarios. Los usuarios pueden hablar al micrófono de su dispositivo wearable y guardar la transcripción de lo que dicen en una nota. Como un dispositivo wearable generalmente no tiene la potencia de procesamiento ni la capacidad de batería necesarias para controlar la actividad de transcripción de voz, la app debe descargar este trabajo en un dispositivo conectado con mayor capacidad.

En las siguientes secciones, verás cómo anunciar nodos de dispositivos que pueden procesar solicitudes de actividad, identificar los nodos capaces de satisfacer una necesidad solicitada y enviar mensajes a esos nodos.

Si vas a iniciar una actividad en un dispositivo portátil desde un wearable, usa la clase MessageClient para enviar la solicitud. Como se pueden conectar varios wearables al dispositivo portátil, la app para wearables debe determinar que hay un nodo conectado capaz de iniciar la actividad. En tu app para dispositivos portátiles, anuncia que el nodo en el que se ejecuta proporciona funciones específicas.

Para anunciar las funciones de tu app para dispositivos portátiles, realiza lo siguiente:

  1. Crea un archivo de configuración XML en el directorio res/values/ de tu proyecto y asígnale el nombre wear.xml.
  2. Agrega un recurso llamado android_wear_capabilities a wear.xml.
  3. Define las funciones que brinda el dispositivo.

Nota: Las funciones son strings personalizadas que defines y deben ser únicas dentro de tu app.

En el siguiente ejemplo, se muestra cómo agregar una función llamada voice_transcription a wear.xml:

    <resources>
        <string-array name="android_wear_capabilities">
            <item>voice_transcription</item>
        </string-array>
    </resources>
    

Cómo obtener los nodos con las funciones requeridas

Inicialmente, puedes detectar los nodos capaces mediante una llamada al método getCapability de la clase CapabilityClient. En el siguiente ejemplo, se muestra cómo obtener manualmente los resultados de los nodos disponibles con la función 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);
    }
    

Para detectar nodos capaces a medida que se conectan al dispositivo wearable, registra una instancia de un objeto de escucha, específicamente un OnCapabilityChangedListener de un objeto CapabilityClient. En el siguiente ejemplo, se muestra cómo registrar el objeto de escucha y obtener los resultados de los nodos disponibles con la función 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. Named or anonymous classes can also be used.
        CapabilityClient.OnCapabilityChangedListener capabilityListener =
            capabilityInfo -> { updateTranscriptionCapability(capabilityInfo); };
        Wearable.getCapabilityClient(context).addListener(
            capabilityListener,
            VOICE_TRANSCRIPTION_CAPABILITY_NAME);
    }
    

Después de detectar los nodos capaces, determina adónde enviar el mensaje. Deberías elegir un nodo que esté cerca de tu dispositivo wearable para minimizar el enrutamiento de mensajes a través de muchos nodos. Se entiende por nodo cercano aquel que está directamente conectado al dispositivo. Para saber si hay un nodo cercano, llama al método Node.isNearby().

En el siguiente ejemplo, se muestra cómo podrías determinar cuál es el mejor nodo para usar:

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

Cómo entregar el mensaje

Cuando hayas identificado cuál es el mejor nodo para usar, envía el mensaje usando la clase MessageClient.

En el ejemplo a continuación, se muestra cómo enviar un mensaje desde un dispositivo wearable al nodo capaz de realizar la transcripción. Verifica que el nodo esté disponible antes de intentar enviar el mensaje. Esta llamada es síncrona y bloquea el procesamiento hasta que el sistema pone en cola el mensaje para su entrega.

Nota: Un código de resultado exitoso no garantiza que se haya entregado el mensaje. Si tu app requiere confiabilidad de datos, puedes usar objetos DataItem o la clase ChannelClient para enviar datos entre dispositivos.

Kotlin

    const val VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription"
    ...
    private fun requestTranscription(voiceData: ByteArray) {
        transcriptionNodeId?.also { nodeId ->
            val sendTask: 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) {
            Task<Integer> 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
        }
    }
    

Nota: Para obtener más información sobre las llamadas síncronas y asíncronas a los Servicios de Google Play y cuándo usarlas, consulta la API de Tasks.

También puedes transmitir mensajes a todos los nodos conectados. Para recuperar todos los nodos conectados a los que puedes enviar mensajes, implementa el siguiente código:

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

Cómo recibir un mensaje

Si quieres recibir notificaciones de mensajes recibidos, implementa la interfaz MessageClient.OnMessageReceivedListener para proporcionar un objeto de escucha de eventos de mensaje. Luego, registra ese objeto de escucha con el método addListener. En el siguiente ejemplo, se muestra cómo podrías implementar el objeto de escucha para comprobar la VOICE_TRANSCRIPTION_MESSAGE_PATH. Si la condición es true, inicia una actividad para procesar los datos de voz.

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

Este es solo un fragmento que requiere más detalles de implementación. Obtén información sobre cómo implementar una actividad o un servicio completo de objeto de escucha en Cómo escuchar eventos de Data Layer.