Wear에서 메시지 주고받기

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

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

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

여러 개의 웨어러블 기기를 사용자의 휴대기기에 연결할 수 있습니다. 네트워크에서 연결된 각 기기는 노드로 간주됩니다.

연결된 기기가 여러 개인 경우 어떤 노드에서 메시지를 수신할지를 고려해야 합니다. 예를 들어, 웨어러블 기기에서 음성 데이터를 수신하는 음성 텍스트 변환 앱에서는 휴대기기처럼 요청을 처리할 수 있는 처리 능력과 배터리 용량을 갖춘 노드로 메시지를 보냅니다.

참고: 메시지의 세부정보를 지정할 때는 연결된 노드가 여러 개 있을 수 있다는 점을 고려하세요. 메시지가 원하는 기기 또는 노드로 전송되는지 확인합니다.

사용 예는 다음 샘플 앱을 참고하세요.

메시지 보내 줘

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

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

웨어러블 기기에서 휴대기기의 활동을 시작하려면 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() 메서드를 호출합니다. 근처에 노드가 2개 이상 있으면 임의로 하나를 선택하세요. 마찬가지로 근처에 기능이 있는 노드가 없으면 기능이 있는 노드를 임의로 선택하세요.

다음 예는 사용할 최적의 노드를 결정하는 방법을 보여줍니다.

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

이 코드에는 추가 구현 세부정보가 필요합니다. 데이터 영역 이벤트 수신 대기에서 전체 리스너 서비스 또는 활동을 구현하는 방법에 관해 알아보세요.