Отправляйте и получайте сообщения на Wear

Вы отправляете сообщения с помощью API MessageClient и прикрепляете к сообщению следующие элементы:

  • Необязательная произвольная полезная нагрузка
  • Путь, который однозначно идентифицирует действие сообщения

В отличие от элементов данных, синхронизация между приложениями на мобильном устройстве и носимом устройстве не производится. Сообщения — это механизм односторонней связи, полезный для удалённых вызовов процедур (RPC), например, для отправки сообщения на носимое устройство для запуска какого-либо действия.

К одному портативному устройству пользователя можно подключить несколько носимых устройств. Каждое подключенное устройство в сети считается узлом .

При подключении нескольких устройств необходимо учитывать, какие узлы получают сообщения. Например, в приложении для транскрипции голоса, принимающем голосовые данные на носимом устройстве, отправьте сообщение узлу с достаточной вычислительной мощностью и ёмкостью аккумулятора для обработки запроса, например, портативному устройству.

Примечание: При указании деталей сообщения учитывайте возможность подключения нескольких узлов. Убедитесь, что сообщение доставляется на нужные устройства или узлы.

Примеры использования см. в следующем примере приложения: DataLayer

Отправить сообщение

Приложение для носимых устройств может предоставлять пользователям такие функции, как транскрипция голоса. Пользователи могут говорить в микрофон своего устройства и сохранять расшифровку в заметке. Поскольку носимое устройство обычно не обладает достаточной вычислительной мощностью и ёмкостью аккумулятора для выполнения транскрипции голоса, приложению необходимо переложить эту задачу на более мощное подключённое устройство.

В следующих разделах показано, как рекламировать узлы устройств, которые могут обрабатывать запросы активности, обнаруживать узлы, способные удовлетворить запрошенную потребность, и отправлять сообщения этим узлам.

Чтобы запустить действие на мобильном устройстве с носимого устройства, используйте класс MessageClient для отправки запроса. Поскольку к мобильному устройству можно подключить несколько носимых устройств, носимому приложению необходимо определить, способен ли подключенный узел запустить это действие. В мобильном приложении укажите, что узел, на котором оно запущено, предоставляет определенные возможности.

Чтобы рекламировать возможности вашего мобильного приложения, выполните следующие действия:

  1. Создайте XML-файл конфигурации в каталоге res/values/ вашего проекта и назовите его wear.xml .
  2. Добавьте ресурс с именем android_wear_capabilities в wear.xml .
  3. Определите возможности, которые предоставляет устройство.

Примечание: Возможности — это пользовательские строки, которые вы определяете и которые должны быть уникальными в пределах вашего приложения.

В следующем примере показано, как добавить возможность с именем voice_transcription в wear.xml :

<resources xmlns:tools="http://schemas.android.com/tools"
           tools:keep="@array/android_wear_capabilities">
    <string-array name="android_wear_capabilities">
        <item>voice_transcription</item>
    </string-array>
</resources>

Извлечь узлы с требуемыми возможностями

Изначально вы можете определить доступные узлы, вызвав метод getCapability класса CapabilityClient . Для использования этого метода ваше приложение Wear OS и приложение для телефона должны иметь одинаковый идентификатор приложения. В следующем примере показано, как вручную получить результаты о доступных узлах с помощью возможности voice_transcription :

Котлин

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

Ява

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

Внимание: Перед использованием API Wearable Data Layer убедитесь, что он доступен на устройстве; в противном случае возникнет исключение. Используйте класс GoogleApiAvailability , как реализовано в Horologist .

Чтобы обнаружить доступные узлы при их подключении к носимому устройству, зарегистрируйте экземпляр прослушивателя, а именно OnCapabilityChangedListener объекта CapabilityClient . В следующем примере показано, как зарегистрировать прослушиватель и получить результат с доступными узлами, имеющими возможность voice_transcription :

Котлин

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

Ява

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

После обнаружения подходящих узлов определите, куда отправить сообщение. Выберите узел, находящийся в непосредственной близости от вашего носимого устройства, чтобы минимизировать маршрутизацию сообщений через несколько узлов. Ближайшим узлом считается узел, напрямую подключенный к устройству. Чтобы определить, находится ли узел поблизости, например, подключен по Bluetooth, вызовите метод Node.isNearby() . Если поблизости находится несколько узлов, выберите один из них произвольно; аналогично, если поблизости нет подходящих узлов, выберите подходящий узел произвольно.

В следующем примере показано, как можно определить лучший узел для использования:

Котлин

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
}

Ява

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

Донесите сообщение

Определив узел для использования, отправьте сообщение с помощью класса MessageClient .

В следующем примере показано, как отправить сообщение с носимого устройства на узел, поддерживающий транскрипцию. Этот вызов является синхронным и блокирует обработку, пока система не поставит сообщение в очередь на доставку.

Примечание: Успешный код результата не гарантирует доставку сообщения. Если вашему приложению требуется надёжность данных, рассмотрите возможность использования объектов DataItem или класса ChannelClient для передачи данных между устройствами.

Котлин

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

Ява

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

Примечание: чтобы узнать больше об асинхронных и синхронных вызовах сервисов Google Play, а также о том, когда следует использовать каждый из них, см . API задач .

Вы также можете рассылать сообщения всем подключённым узлам. Чтобы получить список всех подключённых узлов, которым можно отправлять сообщения, реализуйте следующий код:

Котлин

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

Ява

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

Получить сообщение

Чтобы получать уведомления о полученных сообщениях, реализуйте интерфейс MessageClient.OnMessageReceivedListener , чтобы предоставить прослушиватель событий, связанных с сообщениями. Затем зарегистрируйте прослушиватель с помощью метода addListener . В следующем примере показано, как реализовать прослушиватель для проверки VOICE_TRANSCRIPTION_MESSAGE_PATH . Если это условие true , запустите действие для обработки голосовых данных.

Котлин

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

Ява

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

Для этого кода требуется более подробная реализация. Подробнее о реализации полноценной службы или активности прослушивателя см. в статье Прослушивание событий уровня данных .