Wear でデータレイヤ イベントを処理する

Data Layer API を呼び出した場合、呼び出しの完了時にそのステータスを受け取ることができます。また、Wear OS by Google ネットワーク上の任意の場所でアプリが行ったデータ変更に起因するデータイベントをリッスンできます。

注: Wear アプリは Data Layer API を使用してスマートフォン アプリと通信できますが、この API を使用したネットワークへの接続は推奨されません。

以下の関連リソースもご覧ください。

Data Layer API の呼び出しステータスを待機する

Data Layer API の呼び出し( DataClient クラスの putDataItem メソッドを使用した呼び出しなど)では、 Task<ResultType> オブジェクトが返されることがあります。Task オブジェクトが作成されるとすぐに、操作がバックグラウンドでキューに登録されます。この後にさらに実行することがない場合、操作は最終的に、サイレントに完了します。しかし通常は、操作の完了後に結果に基づいてなんらかの処理を行う必要があるため、Task オブジェクトにより、同期的または非同期的に結果のステータスを待機させられます。

非同期呼び出し

コードがメインの UI スレッドで実行されている場合は、Data Layer API の呼び出しをブロックしないでください。呼び出しを非同期的に実行するには、操作の完了時に呼び出される Task オブジェクトにコールバック メソッドを追加します。

Kotlin

    // Using Kotlin function references
    task.addOnSuccessListener(::handleDataItem)
    task.addOnFailureListener(::handleDataItemError)
    task.addOnCompleteListener(::handleTaskComplete)
    ...
    fun handleDataItem(dataItem: DataItem) { ... }
    fun handleDataItemError(exception: Exception) { ... }
    fun handleTaskComplete(task: Task<DataItem>) { ... }
    

Java

    // Using Java 8 Lambdas.
    task.addOnSuccessListener(dataItem -> handleDataItem(dataItem));
    task.addOnFailureListener(exception -> handleDataItemError(exception));
    task.addOnCompleteListener(task -> handleTaskComplete(task));
    

各種タスクを連続して実行できるかどうかなど、その他の実行可能な操作については、Task API をご覧ください。

同期呼び出し

コードがバックグラウンド サービスの独立したハンドラ スレッドで実行されている場合( WearableListenerService のケース)、呼び出しをブロックしても問題ありません。この場合は、Task オブジェクトで Tasks.await() を呼び出すことができます。これにより、リクエストが完了するまでブロックされ、Result オブジェクトが返されます。

Kotlin

    try {
        Tasks.await(dataItemTask).apply {
            Log.d(TAG, "Data item set: $uri")
        }
    }
    catch (e: ExecutionException) { ... }
    catch (e: InterruptedException) { ... }
    

Java

    try {
        DataItem item = Tasks.await(dataItemTask);
        Log.d(TAG, "Data item set: " + item.getUri());
    } catch (ExecutionException | InterruptedException e) {
      ...
    }
    

データレイヤ イベントをリッスンする

データレイヤはハンドヘルドとウェアラブル間でデータの同期と送信を行うため、通常は重要なイベントをリッスンする必要があります。このようなイベントの例として、データアイテムの作成やメッセージの受信などがあります。

データレイヤ イベントをリッスンする方法には次の 2 つがあります。

どちらの方法でも、処理対象のイベントのデータイベント コールバック メソッドをオーバーライドします。

注: 電池使用量に関しては、 WearableListenerService がアプリのマニフェストに登録されているため、アプリがまだ実行されていない場合はこのサービスで起動できます。アプリがすでに実行されているときにイベントのリッスンのみを行う必要がある場合(インタラクティブなアプリの場合によくあるケース)、WearableListenerService は使用しないでください。その代わり、たとえば DataClient クラスの addListener メソッドを使用してライブリスナーを登録します。これにより、システムにかかる負荷と電池使用量を削減できます。

WearableListenerService を使用する

通常は、ウェアラブル アプリとハンドヘルド アプリの両方でこのサービスのインスタンスを作成します。これらのアプリのいずれかでデータイベントを処理しない場合、そのアプリにはこのサービスを実装する必要はありません。

たとえば、データアイテム オブジェクトの設定と取得を行うハンドヘルド アプリと、それらのオブジェクトの更新をリッスンしてその UI を更新するウェアラブル アプリがあるとします。ウェアラブルはデータアイテムを一切更新しないため、ハンドヘルド アプリはウェアラブル アプリからのデータイベントをリッスンしません。

WearableListenerService を使用してリッスン可能なイベントの一部を以下に示します。

  • onDataChanged(): データアイテム オブジェクトが作成、削除、変更されたときに、接続されているすべてのノードでこのコールバックがシステムによってトリガーされます。
  • onMessageReceived(): ノードからメッセージが送信されたときに、ターゲット ノードでこのコールバックがトリガーされます。
  • onCapabilityChanged(): アプリのインスタンスがアドバタイズする機能をネットワークで使用できるようになったときに、イベントによってこのコールバックがトリガーされます。近くのノードを探している場合は、コールバックで提供されるノードの isNearby() メソッドに対してクエリを実行できます。

上記のイベントだけでなく、 ChannelClient.ChannelCallback からのイベント(onChannelOpened() など)もリッスンできます。

上記のイベントはすべて、メインスレッドではなくバックグラウンド スレッドで実行されます。

WearableListenerService を作成する手順は次のとおりです。

  1. WearableListenerService を拡張するクラスを作成します。
  2. 関心のあるイベント(onDataChanged() など)をリッスンします。
  3. Android マニフェストでインテント フィルタを宣言し、WearableListenerService についてシステムに通知します。この宣言により、システムで必要に応じてサービスをバインドできるようになります。

次の例は、シンプルな WearableListenerService の実装方法を示しています。

Kotlin

    private const val TAG = "DataLayerSample"
    private const val START_ACTIVITY_PATH = "/start-activity"
    private const val DATA_ITEM_RECEIVED_PATH = "/data-item-received"

    class DataLayerListenerService : WearableListenerService() {

        override fun onDataChanged(dataEvents: DataEventBuffer) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "onDataChanged: $dataEvents")
            }

            // Loop through the events and send a message
            // to the node that created the data item.
            dataEvents.map { it.dataItem.uri }
                    .forEach { uri ->
                        // Get the node id from the host value of the URI
                        val nodeId: String = uri.host
                        // Set the data of the message to be the bytes of the URI
                        val payload: ByteArray = uri.toString().toByteArray()

                        // Send the RPC
                        Wearable.getMessageClient(this)
                                .sendMessage(nodeId, DATA_ITEM_RECEIVED_PATH, payload)
                    }
        }
    }
    

Java

    public class DataLayerListenerService extends WearableListenerService {
        private static final String TAG = "DataLayerSample";
        private static final String START_ACTIVITY_PATH = "/start-activity";
        private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";

        @Override
        public void onDataChanged(DataEventBuffer dataEvents) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "onDataChanged: " + dataEvents);
            }

            // Loop through the events and send a message
            // to the node that created the data item.
            for (DataEvent event : dataEvents) {
                Uri uri = event.getDataItem().getUri();

                // Get the node id from the host value of the URI
                String nodeId = uri.getHost();
                // Set the data of the message to be the bytes of the URI
                byte[] payload = uri.toString().getBytes();

                // Send the RPC
                Wearable.getMessageClient(this).sendMessage(
                      nodeId,  DATA_ITEM_RECEIVED_PATH, payload);
            }
        }
    }
    

次のセクションでは、このリスナーとともにインテント フィルタを使用する方法について説明します。

WearableListenerService とともにフィルタを使用する

前のセクションで示した WearableListenerService の例のインテント フィルタは次のようになります。

    <service android:name=".DataLayerListenerService">
      <intent-filter>
          <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
          <data android:scheme="wear" android:host="*"
                   android:path="/start-activity" />
      </intent-filter>
    </service>
    

このフィルタでは、以前におすすめした BIND_LISTENER アクションが DATA_CHANGED アクションで置き換えられています。これにより、特定のイベントの場合にのみ、アプリが復帰または開始されます。この変更により、システムの効率性を改善し、電池の消費量およびアプリに関連するその他のオーバーヘッドを削減できます。この例では、スマートウォッチが /start-activity データアイテムをリッスンし、スマートフォンが /data-item-received メッセージのレスポンスをリッスンします。

フィルタのマッチングには Android の標準のルールが適用されます。マニフェストごとに複数のサービスを、サービスごとに複数のインテント フィルタを、フィルタごとに複数のアクションを、フィルタごとに複数のデータスタンザを指定できます。フィルタのマッチングは、ワイルドカード ホストまたは特定のホストで行うことができます。ワイルドカード ホストでマッチングを行うには、host="*" を使用します。一方、特定のホストでマッチングを行うには、host=<node_id> を指定します。

リテラルパスまたはパス プレフィックスをマッチングすることも可能です。パスまたはパス プレフィックスによるマッチングでは、ワイルドカード ホストまたは特定のホストを指定する必要があります。そうしないと、システムは指定されたパスを無視します。

Wear でサポートされているフィルタのタイプについて詳しくは、WearableListenerService の API リファレンス ドキュメントをご覧ください。

データフィルタとマッチング ルールについて詳しくは、data マニフェスト要素の API リファレンス ドキュメントをご覧ください。

インテント フィルタのマッチングに関しては、以下の 2 つの重要なルールを覚えておいてください。

  • インテント フィルタのスキームが指定されていない場合、システムは他の URI 属性をすべて無視します。
  • フィルタのホストが指定されていない場合、システムはパスの属性をすべて無視します。

ライブリスナーを使用する

ユーザーがアプリを操作しているときにアプリがデータレイヤ イベントのみを監視している場合は、サービスを長時間実行してすべてのデータ変更に対処する必要はありません。このような場合、以下のインターフェースのうち 1 つ以上を実装することで、アクティビティ内でイベントをリッスンできます。

データイベントをリッスンするアクティビティを作成する手順は次のとおりです。

  1. 目的のインターフェースを実装します。
  2. onCreate() または onResume() メソッドで、Wearable.getDataClient(this).addListener()MessageClient.addListener()CapabilityClient.addListener()ChannelClient.registerChannelCallback() のいずれかを呼び出して、アクティビティがデータレイヤ イベントをリッスンすることに関心があることを Google Play 開発者サービスに通知します。
  3. onStop() または onPause() で、DataClient.removeListener()MessageClient.removeListener()CapabilityClient.removeListener()ChannelClient.unregisterChannelCallback() のいずれかを使用してリスナーの登録を解除します。
  4. アクティビティの関心の対象が特定のパス プレフィックスを持つイベントのみの場合は、適切なプレフィックス フィルタが指定されたリスナーを追加することで、現在のアプリの状態に関連するデータのみを受け取ることができます。
  5. 実装したインターフェースに応じて、onDataChanged()onMessageReceived()onCapabilityChanged()、または ChannelClient.ChannelCallback のメソッドを実装します。これらのメソッドはメインスレッドで呼び出されます。または、WearableOptions を使用してカスタムの Looper を指定することもできます。

以下に、DataClient.OnDataChangedListener の実装例を示します。

Kotlin

    class MainActivity : Activity(), DataClient.OnDataChangedListener {

        public override fun onResume() {
            Wearable.getDataClient(this).addListener(this)
        }

        override fun onPause() {
            Wearable.getDataClient(this).removeListener(this)
        }

        override fun onDataChanged(dataEvents: DataEventBuffer) {
            dataEvents.forEach { event ->
                if (event.type == DataEvent.TYPE_DELETED) {
                    Log.d(TAG, "DataItem deleted: " + event.dataItem.uri)
                } else if (event.type == DataEvent.TYPE_CHANGED) {
                    Log.d(TAG, "DataItem changed: " + event.dataItem.uri)
                }
            }
        }
    }
    

Java

    public class MainActivity extends Activity implements DataClient.OnDataChangedListener {

        @Override
        public void onResume() {
            Wearable.getDataClient(this).addListener(this);
        }

        @Override
        protected void onPause() {
            Wearable.getDataClient(this).removeListener(this);
        }

        @Override
        public void onDataChanged(DataEventBuffer dataEvents) {
            for (DataEvent event : dataEvents) {
                if (event.getType() == DataEvent.TYPE_DELETED) {
                    Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
                } else if (event.getType() == DataEvent.TYPE_CHANGED) {
                    Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
                }
            }
        }
    }
    

ライブリスナーとともにフィルタを使用する

このページですでに説明したように、マニフェスト ベースの WearableListenerService オブジェクトに対してインテント フィルタを指定できるのと同様に、Wearable API を介してライブリスナーを登録する際にインテント フィルタを使用できます。API ベースのライブリスナーとマニフェスト ベースのリスナーの両方に同じルールを適用できます。

一般的なパターンとしては、アクティビティの onResume() メソッドで特定のパスまたはパス プレフィックスを指定してリスナーを登録し、アクティビティの onPause() メソッドでリスナーを削除します。この方法でリスナーを実装すると、アプリでより選択的にイベントを受信できるため、アプリの設計と効率性を改善することができます。