Wear에서 메시지 주고받기

MessageClient API를 사용하여 메시지를 보내고 메시지에 다음 항목을 첨부합니다.

  • 임의의 페이로드(선택사항)
  • 메시지의 작업을 고유하게 식별하는 경로

데이터 항목의 경우와 달리 핸드헬드 및 웨어러블 앱 간에 동기화가 발생하지 않습니다. 메시지는 리모트 프로시져 콜(RPC)에 도움이 되는 단방향 통신 메커니즘입니다. 예를 들면 활동을 시작하기 위해 웨어러블로 메시지를 보내는 것입니다.

여러 개의 웨어러블 기기를 사용자의 핸드헬드 기기에 연결할 수 있습니다. 네트워크에서 연결된 각 기기는 노드로 간주됩니다. 연결된 기기가 여러 개인 경우 어떤 노드에서 메시지를 수신할지를 고려해야 합니다. 예를 들어, 웨어러블 기기에서 음성 데이터를 수신하는 음성 텍스트 변환 앱에서는 핸드헬드 기기처럼 요청을 처리할 수 있는 처리 능력과 배터리 용량을 갖춘 노드로 메시지를 보내야 합니다.

참고: 7.3.0 이전의 Google Play 서비스 버전에서는 하나의 웨어러블 기기를 한 번에 하나의 핸드헬드 기기에만 연결할 수 있습니다. 여러 개의 연결된 노드 기능이 필요한 경우 기존 코드를 업데이트해야 할 수 있습니다. 변경 사항을 구현하지 않으면 메시지가 의도한 기기로 전달되지 않을 수 있습니다.

다음의 관련 리소스를 참조하세요.

메시지 보내기

웨어러블 앱은 음성 텍스트 변환과 같은 기능을 사용자에게 제공할 수 있습니다. 사용자는 웨어러블 기기의 마이크에 대고 말하고, 텍스트 변환을 메모로 저장할 수 있습니다. 웨어러블 기기에는 일반적으로 음성 텍스트 변환 활동을 처리하는 데 필요한 처리 능력 및 배터리 용량이 없으므로, 앱이 좀 더 역량을 갖춘 연결된 기기로 이 작업을 오프로드해야 합니다.

다음 섹션에서는 활동 요청을 처리하고, 요청된 요구 사항을 충족할 수 있는 노드를 찾은 다음, 해당 노드에 메시지를 보낼 수 있는 기기 노드를 알리는 방법을 보여줍니다.

웨어러블 기기로부터 핸드헬드 기기의 활동을 시작하려면 MessageClient 클래스를 사용하여 요청을 보냅니다. 핸드헬드 기기에 여러 웨어러블을 연결할 수 있으므로, 웨어러블 앱은 연결된 노드가 활동을 시작할 수 있는지를 확인해야 합니다. 핸드헬드 앱에서는 앱이 실행 중인 노드에서 특정 기능을 제공함을 알립니다.

핸드헬드 앱의 기능을 알리려면 다음 단계를 따릅니다.

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

자바

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

자바

    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
    }
    

자바

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

자바

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

자바

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

자바

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

위 예제는 구현 세부정보가 더 많이 필요한 스니펫일 뿐입니다. 데이터 영역 이벤트 수신에서 전체 리스너 서비스 또는 활동을 구현하는 방법에 관해 알아보세요.