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
を拡張するサービスを作成する。- アクティビティまたはクラスを作成して、
DataClient.OnDataChangedListener
インターフェースを実装する。
どちらの方法でも、処理対象のイベントのデータイベント コールバック メソッドをオーバーライドします。
注: 電池使用量に関しては、
WearableListenerService
がアプリのマニフェストに登録されているため、アプリがまだ実行されていない場合でも、このサービスでアプリを起動できます。アプリがすでに実行されているときにイベントのリッスンのみを行う必要がある場合(インタラクティブなアプリの場合によくあるケース)、WearableListenerService
は使用しないでください。その代わり、たとえば DataClient
クラスの addListener
メソッドを使用して、ライブリスナーを登録します。これにより、システムにかかる負荷と電池使用量を削減できます。
WearableListenerService を使用する
通常は、ウェアラブル アプリとハンドヘルド アプリの両方でこのサービスのインスタンスを作成します。これらのアプリのいずれかでデータイベントを処理しない場合、そのアプリにはこのサービスを実装する必要はありません。
たとえば、データアイテム オブジェクトの設定と取得を行うハンドヘルド アプリと、そのオブジェクトの更新をリッスンして UI を更新するウェアラブル アプリがあるとします。この場合、ウェアラブル アプリがデータアイテムを更新することはないため、ハンドヘルド アプリはウェアラブル アプリのデータイベントをリッスンしません。
WearableListenerService
を使用してリッスン可能な主なイベントを以下に示します。
onDataChanged()
: データアイテム オブジェクトが作成、削除、変更されたときに、接続されているすべてのノードでこのコールバックがシステムによってトリガーされます。onMessageReceived()
: ノードからメッセージが送信されたときに、ターゲット ノードでこのコールバックがトリガーされます。onCapabilityChanged()
: アプリのインスタンスがアドバタイズする機能をネットワークで使用できるようになったときに、そのイベントによってこのコールバックがトリガーされます。近くにあるノードを探している場合、コールバックで提供されたノードのisNearby()
メソッドに対してクエリを実行できます。
上記のイベントに加えて、
ChannelClient.ChannelCallback
からのイベント(onChannelOpened()
など)もリッスンできます。
上記のイベントはすべて、メインスレッドではなくバックグラウンド スレッドで実行されます。
WearableListenerService
を作成する手順は次のとおりです。
WearableListenerService
を拡張するクラスを作成します。- 対象のイベント(
onDataChanged()
など)をリッスンします。 - 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 つ以上を実装することで、アクティビティ内でイベントをリッスンできます。
DataClient.OnDataChangedListener
MessageClient.OnMessageReceivedListener
CapabilityClient.OnCapabilityChangedListener
ChannelClient.ChannelCallback
データイベントをリッスンするアクティビティを作成する手順は次のとおりです。
- 目的のインターフェースを実装します。
onCreate()
メソッドまたはonResume()
メソッドで、Wearable.getDataClient(this).addListener()
、MessageClient.addListener()
、CapabilityClient.addListener()
、ChannelClient.registerChannelCallback()
のいずれかを呼び出して、アクティビティがデータレイヤ イベントをリッスンすることに関心があることを Google Play 開発者サービスに通知します。onStop()
またはonPause()
で、DataClient.removeListener()
、MessageClient.removeListener()
、CapabilityClient.removeListener()
、ChannelClient.unregisterChannelCallback()
のいずれかを使用してリスナーの登録を解除します。- アクティビティの関心の対象が特定のパス プレフィックスを持つイベントだけの場合は、適切なプレフィックス フィルタを指定したリスナーを追加することで、現在のアプリの状態に関するデータだけを受け取ることができます。
- 実装したインターフェースに応じて、
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()
メソッドでリスナーを削除します。この方法でリスナーを実装すると、アプリが選択的にイベントを受信できるようになるため、アプリの設計と効率性を改善することができます。