在 Wear 上发送和接收消息

您可以使用 MessageClient API 发送消息,并将以下项目附加到消息

  • 可选的任意有效负载
  • 唯一标识消息操作的路径

与数据项目不同,手持设备和可穿戴设备应用之间不会进行同步。消息是一种单向通信机制,适用于远程过程调用 (RPC),例如向可穿戴设备发送消息以启动活动。

多个可穿戴设备可以连接到用户的手机设备。网络中的每个连接设备都被视为一个节点

对于多个连接的设备,您必须考虑哪些节点会接收消息。例如,在一个接收可穿戴设备上的语音数据的语音转录应用中,将消息发送到具有处理能力和电池容量来处理请求的节点,例如手机设备。

注意: 当您指定消息的详细信息时,请考虑可能存在多个连接的节点。确保消息传递到目标设备或节点。

有关使用示例,请参阅以下示例应用: DataLayer

发送消息

可穿戴设备应用可以为用户提供功能,例如语音转录。用户可以对着可穿戴设备的麦克风说话,并将转录内容保存到笔记中。由于可穿戴设备通常没有处理语音转录活动所需的处理能力和电池容量,因此应用需要将这项工作卸载到连接的更强大的设备上。

以下部分将向您展示如何宣传可以处理活动请求的设备节点,发现能够满足请求需求的节点,以及向这些节点发送消息。

要从可穿戴设备启动手机设备上的活动,请使用 MessageClient 类发送请求。由于多个可穿戴设备可以连接到手机设备,因此可穿戴设备应用需要确定连接的节点是否能够启动活动。在您的手机应用中,宣传运行该节点的节点提供了特定功能。

要宣传您的手机应用的功能,请执行以下操作

  1. 在项目的 res/values/ 目录中创建一个 XML 配置文件,并将其命名为 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>

检索具有所需功能的节点

最初,您可以通过调用 CapabilityClient 类的 getCapability 方法来检测有能力的节点。要使用此方法,您的 Wear OS 应用和手机应用必须具有相同的应用 ID。以下示例展示了如何手动检索具有 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);
}

要检测连接到可穿戴设备的有能力的节点,请注册一个侦听器实例,具体来说是 CapabilityClient 对象的 OnCapabilityChangedListener。以下示例展示了如何注册侦听器并检索具有 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);
}

在检测到有能力的节点后,确定要将消息发送到哪里。选择一个靠近您的可穿戴设备的节点,以最大程度地减少消息通过多个节点的路由。附近的节点定义为直接连接到设备的节点。要确定节点是否在附近(例如通过蓝牙连接),请调用 Node.isNearby() 方法。如果有多个节点在附近,请任意选择一个;类似地,如果附近没有有能力的节点,则任意选择一个有能力的节点。

以下示例展示了如何确定要使用的最佳节点

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

传递消息

确定要使用的节点后,请使用 MessageClient 类发送消息。

以下示例展示了如何从可穿戴设备向能够转录的节点发送消息。此调用是同步的,会阻塞处理,直到系统将消息排队以进行传递。

注意: 成功的结果代码不能保证消息会传递。如果您的应用需要数据可靠性,请考虑使用 DataItem 对象或 ChannelClient 类在设备之间发送数据。

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

注意: 要详细了解对 Google Play 服务的异步和同步调用,以及何时使用它们,请参阅 Tasks API

您还可以向所有连接的节点广播消息。要检索可以发送消息的所有连接节点,请实施以下代码

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

接收消息

要接收已接收消息的通知,请实现 MessageClient.OnMessageReceivedListener 接口,以提供用于消息事件的侦听器。然后,使用 addListener 方法注册侦听器。以下示例展示了如何实现侦听器以检查 VOICE_TRANSCRIPTION_MESSAGE_PATH。如果此条件为 true,则启动一个活动来处理语音数据。

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

此代码需要更多实现细节。了解如何在 侦听数据层事件 中实现完整的侦听器服务或活动。