Wear でメッセージを送受信する

MessageClient API を使用してメッセージを送信し、以下のアイテムをメッセージに添付します。

  • 省略可能な任意のペイロード
  • メッセージのアクションを一意に識別するパス

データアイテムの場合とは異なり、ハンドヘルド アプリとウェアラブル アプリ間で同期は行われません。メッセージは一方向の通信メカニズムであり、ウェアラブルにメッセージを送信してアクティビティを開始するなどのリモート プロシージャ コール(RPC)に役立ちます。

1 台のハンドヘルド デバイスに対して複数台のウェアラブル デバイスを接続できます。ネットワークに接続された各デバイスはノードとみなされます。

複数台のデバイスが接続されている場合、どのノードでメッセージを受信するかを考える必要があります。たとえば、ウェアラブル デバイスで音声データを受け取る音声文字変換アプリでは、リクエストを処理できる処理能力とバッテリー容量を持つノード(ハンドヘルド デバイスなど)にメッセージを送信します。

注: メッセージの詳細を指定するときは、複数のノードが接続されている可能性があることを考慮し、意図したデバイスまたはノードにメッセージが配信されるようにしてください。

使用例については、次のサンプルアプリをご覧ください。

メッセージを送信する

ウェアラブル アプリでは、音声文字変換などの機能をユーザーに提供できます。ユーザーはウェアラブル デバイスのマイクに話しかけて、音声文字変換された内容をメモに保存できます。ウェアラブル デバイスは通常、音声文字変換のアクティビティを処理するために必要な処理能力とバッテリー容量を備えていないため、アプリはこの処理をより高性能の接続済みデバイスにオフロードする必要があります。

以下のセクションでは、アクティビティのリクエストを処理できるデバイスノードをアドバタイズする方法、要求されたニーズに対応できるノードを見つける方法、それらのノードにメッセージを送信する方法について説明します。

ウェアラブル デバイスからハンドヘルド デバイス上でアクティビティを起動するには、 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);
}

必要な機能を備えたノードを検出したら、メッセージの送信先を確認します。複数のノードを経由するメッセージ ルーティングを最小限に抑えるには、ウェアラブル デバイスの近くにあるノードを選択します。近くにあるノードとは、デバイスに直接接続されているノードのことです。Bluetooth 経由で接続されているなど、ノードが近くにあるかどうかを確認するには、 Node.isNearby() メソッドを呼び出します。近くに複数のノードがある場合は、任意のノードを 1 つ選択します。同様に、必要な機能を備えたノードが近くにない場合は、必要な機能を備えたノードを任意に選択します。

最適なノードを特定する方法の例を以下に示します。

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

このコードにはより詳細な実装が必要です。完全なリスナーを備えたサービスやアクティビティの実装方法については、データレイヤ イベントをリッスンするをご覧ください。