在 Wear 上发送和接收消息

您可以使用 MessageClient API 发送消息并将以下内容附到消息上:

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

与数据项的情况不同,手持式设备应用和穿戴式设备应用之间不会发生同步。消息是一种单向通信机制,适用于远程过程调用 (RPC),如将消息发送到穿戴式设备以启动 Activity。

可将多部穿戴式设备连接到用户的手持式设备。网络中连接的每部设备被视为一个节点。如果连接了多部设备,您必须考虑哪些节点接收消息。例如,在穿戴式设备上接收语音数据的语音转录应用中,您应将消息发送到具有处理请求的处理能力和电池容量的节点,如手持式设备。

注意:对于低于 7.3.0 的 Google Play 服务版本,一次只能将一部穿戴式设备连接到手持式设备。您可能需要更新现有代码,以将多个连接的节点功能考虑在内。如果您不实现这些变更,您的消息可能无法传送到目标设备。

请参阅以下相关资源:

发送消息

穿戴式设备应用可以为用户提供语音转录等功能。用户可以对着他们的穿戴式设备的麦克风讲话,并将转录内容保存到记事中。由于穿戴式设备通常不具备处理语音转录 Activity 所需的处理能力和电池容量,因此应用应将此工作分担给连接的能力更强的设备。

下面几部分介绍如何公布可处理 Activity 请求的设备节点,发现能够满足需求的节点,并将消息发送到这些节点。

要从穿戴式设备启动手持式设备上的 Activity,请使用 MessageClient 类发送请求。由于可将多部穿戴式设备连接到手持式设备,因此穿戴式设备应用需要确定连接的节点是否能够启动该 Activity。在手持式设备应用中,公布运行它的节点可提供特定功能。

要公布手持式设备应用的功能,请执行以下操作:

  1. 在项目的 res/values/ 目录中创建一个 XML 配置文件,并将其命名为 wear.xml
  2. 将一个名为 android_wear_capabilities 的资源添加到 wear.xml
  3. 定义设备提供的功能。

注意:功能是您定义的自定义字符串,且在您的应用中必须是唯一的。

以下示例展示了如何将一项名为 voice_transcription 的功能添加到 wear.xml

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

检索具有所需功能的节点

首先,您可以通过调用 CapabilityClient 类的 getCapability 方法来检测能够胜任的节点。以下示例展示了如何手动检索具有 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. Named or anonymous classes can also be used.
        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,则启动一个 Activity 来处理语音数据。

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

这只是一个代码段,还需要更多实现详情。要了解如何实现完整的监听器服务或 Activity,请参阅监听数据层事件