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

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

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

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

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 OS がサポートするフィルタタイプの詳細については、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() メソッドでリスナーを削除します。この方法でリスナーを実装すると、アプリが選択的にイベントを受信できるようになるため、アプリの設計と効率性を改善できます。