Data Layer API を呼び出した場合、呼び出しの完了時にそのステータスを受け取ることができます。また、Wear OS by Google ネットワーク上の任意の場所でアプリが行ったデータ変更に起因するデータイベントをリッスンできます。
Data Layer API の効果的な使用例については、Android DataLayer サンプルアプリをご覧ください。
Data Layer API 呼び出しのステータスを待機する
Data Layer API の呼び出し(DataClient クラスの putDataItem メソッドを使用した呼び出しなど)では、Task<ResultType> オブジェクトが返されることがあります。Task オブジェクトが作成されるとすぐに、処理がバックグラウンドでキューに登録されます。この後にさらに実行することがない場合、操作は最終的に警告を出さずに完了します。
しかし通常は、操作の完了後に結果に基づいてなんらかの処理を行う必要があるため、Task オブジェクトにより、非同期的または同期的に結果のステータスを待機できます。
非同期呼び出し
コードがメイン UI スレッドで実行されている場合は、Data Layer API の呼び出しをブロックしないでください。コルーチンを使用して putDataItem を呼び出します。
private suspend fun Context.sendDataAsync(count: Int) { try { val putDataReq: PutDataRequest = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } val dataItem = Wearable.getDataClient(this).putDataItem(putDataReq).await() handleDataItem(dataItem) } catch (e: Exception) { handleDataItemError(e) } finally { handleTaskComplete() } } private fun handleDataItem(dataItem: DataItem) { } private fun handleDataItemError(exception: Exception) { } private fun handleTaskComplete() { }
その他の実行可能な操作(各種タスクの連続実行など)については、Task API をご覧ください。
同期呼び出し
バックグラウンド サービス内(WearableListenerService 内など)の別個のハンドラ スレッドでコードが実行されている場合は、runBlocking を使用して putDataItem へのブロック呼び出しを行います。
注: このメソッドはメインスレッドで呼び出さないでください。
private fun Context.sendDataSync(count: Int) = runBlocking { val putDataReq = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } try { val result = Wearable.getDataClient(this@sendDataSync) .putDataItem(putDataReq) .await() // Logic for success } catch (e: Exception) { // Handle failure } }
データレイヤ イベントをリッスンする
データレイヤはハンドヘルド デバイスとウェアラブル デバイスの間でデータの同期と送信を行うので、通常はデータアイテムの作成やメッセージの受信などの重要なイベントをリッスンする必要があります。
データレイヤ イベントをリッスンする方法には次の 2 つがあります。
WearableListenerServiceを拡張するサービスを作成します。DataClient.OnDataChangedListenerインターフェースを実装するアクティビティまたはクラスを作成します。
どちらの方法でも、処理対象のイベントのデータイベント コールバック メソッドをオーバーライドします。
注: リスナー実装を選択する際は、アプリのバッテリー使用量を考慮してください。WearableListenerService はアプリのマニフェストに登録されるため、アプリがまだ実行されていない場合でも、このサービスでアプリを起動できます。アプリがすでに実行されているときにイベントのリッスンのみを行う必要がある場合(これはインタラクティブなアプリでよくあります)、WearableListenerService は使用しないでください。代わりに、ライブリスナーを登録します。たとえば、DataClient クラスの addListener メソッドを使用します。これにより、システムにかかる負荷とバッテリー使用量を削減できます。
WearableListenerService を使用する
通常は、ウェアラブル アプリとハンドヘルド アプリの両方で WearableListenerService のインスタンスを作成します。ただし、いずれか一方のアプリでデータイベントを処理しない場合、そのアプリにサービスを実装する必要はありません。
たとえば、データアイテム オブジェクトの設定と取得を行うハンドヘルド アプリと、そのオブジェクトの更新をリッスンして UI を更新するウェアラブル アプリがあるとします。ウェアラブル アプリがデータアイテムを更新することはないため、ハンドヘルド アプリはウェアラブル アプリのデータイベントをリッスンしません。
WearableListenerService を使用してリッスンできるイベントを次にいくつか示します。
onDataChanged(): データアイテム オブジェクトが作成、削除、変更されたとき、接続されているすべてのノードで、システムによりこのコールバックがトリガーされます。onMessageReceived(): ノードからメッセージが送信されたときに、ターゲット ノードでこのコールバックがトリガーされます。onCapabilityChanged(): アプリのインスタンスがアドバタイズする機能がネットワークで使用可能になったとき、そのイベントによりこのコールバックがトリガーされます。近くにあるノードを探している場合、コールバックで提供されたノードのisNearby()メソッドに対してクエリを実行できます。
ChannelClient.ChannelCallback からのイベント(onChannelOpened() など)をリッスンすることもできます。
上記のイベントはすべて、メインスレッドではなくバックグラウンド スレッドで実行されます。
WearableListenerService を作成する手順は次のとおりです。
WearableListenerServiceを拡張するクラスを作成します。- 対象のイベント(
onDataChanged()など)をリッスンします。 - Android マニフェスト内でインテント フィルタを宣言して、
WearableListenerServiceについてシステムに通知します。この宣言により、必要に応じてシステムがサービスをバインドできるようになります。
次の例は、WearableListenerService の実装方法を示しています。
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 ) } } }
次のセクションでは、このリスナーとともにインテント フィルタを使用する方法について説明します。
WearableListenerService とともにフィルタを使用する
前のセクションで示した WearableListenerService サンプルのインテント フィルタは次のようになります。
<service android:name=".snippets.datalayer.DataLayerListenerService" android:exported="true" tools:ignore="ExportedService" > <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>
DATA_CHANGED アクション フィルタは、アプリがデータレイヤ イベントに関心があることをシステムに伝えます。
この例では、スマートウォッチが /start-activity データアイテムをリッスンし、スマートフォンが /data-item-received(DATA_ITEM_RECEIVED_PATH)メッセージのレスポンスをリッスンします。
フィルタのマッチングには Android の標準のルールが適用されます。マニフェストごとに複数のサービスを、サービスごとに複数のインテント フィルタを、フィルタごとに複数のアクションを、フィルタごとに複数のデータスタンザを指定できます。フィルタのマッチングは、ワイルドカード ホストまたは特定のホストで行うことができます。ワイルドカード ホストでマッチングを行うには、host="*" を使用します。特定のホストでマッチングを行うには、host=<node_id> を指定します。
リテラルパスまたはパス プレフィックスをマッチングすることも可能です。そのためには、ワイルドカード ホストまたは特定のホストを指定する必要があります。それ以外のパスを指定すると、システムによって無視されます。
Wear OS がサポートするフィルタタイプの詳細については、WearableListenerService の API リファレンス ドキュメントをご覧ください。
データフィルタとマッチング ルールについて詳しくは、API リファレンス ドキュメントの <data> マニフェスト要素をご覧ください。
インテント フィルタをマッチングする際は、次の 2 つの重要なルールに留意してください。
- インテント フィルタのスキームが指定されていない場合、システムは他の URI 属性をすべて無視します。
- フィルタのホストが指定されていない場合、システムはパス属性をすべて無視します。
ライブリスナーを使用する
ユーザーがアプリを操作しているときにアプリがデータレイヤ イベントのみを監視する場合、サービスを長時間実行してすべてのデータ変更を処理する必要はありません。このような場合は、アクティビティ内でイベントをリッスンできます。
よりクリーンで安全なアプローチを推奨するには、Lifecycle Observer を使用します。Lifecycle Observer を使用すると、登録ロジックをアクティビティの onResume() から DefaultLifecycleObserver を実装する再利用可能な別のクラスに移動できます。
このアプローチにより、アクティビティを簡潔に保ち、リスナーの登録解除を忘れるといった一般的なバグを防ぐことができます。
1. ライフサイクル対応リスナーを作成する
このクラスは DataClient.OnDataChangedListener をラップし、Activity のライフサイクルに基づいて独自のサブスクリプションを自動的に管理します。
class WearDataLayerObserver( private val dataClient: DataClient, private val onDataReceived: (DataEventBuffer) -> Unit ) : DefaultLifecycleObserver, DataClient.OnDataChangedListener { // Implementation of the DataClient listener override fun onDataChanged(dataEvents: DataEventBuffer) { onDataReceived(dataEvents) } // Automatically register when the Activity starts override fun onResume(owner: LifecycleOwner) { dataClient.addListener(this) } // Automatically unregister when the Activity pauses override fun onPause(owner: LifecycleOwner) { dataClient.removeListener(this) } }
2. アクティビティでの使用状況
Wear API の場合、アクティビティで onResume() または onPause() をオーバーライドする必要がなくなりました。オブザーバーは onCreate() で一度追加します。
class DataLayerLifecycleActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val dataClient = Wearable.getDataClient(this) // Create the observer and link it to the activity's lifecycle val wearObserver = WearDataLayerObserver(dataClient) { dataEvents -> handleDataEvents(dataEvents) } lifecycle.addObserver(wearObserver) } private fun handleDataEvents(dataEvents: DataEventBuffer) { // ... filter and process events ... } }
この方法が優れている理由:
- Cleaner Activity: Activity ライフサイクル メソッドからボイラープレートを削除します。
- 安全性:
DefaultLifecycleObserverは、アクティビティが予期せず破棄された場合でもリスナーが削除されることを確認し、メモリリークを防ぎます。 - 再利用性: 登録ロジックを書き直すことなく、この
WearDataLayerObserverを任意のアクティビティまたはフラグメントに接続できます。 - 分離: リッスンするタイミングのロジックが、データで何を行うかのロジックから分離されます。
ライブリスナーとともにフィルタを使用する
前述のように、マニフェスト ベースの WearableListenerService オブジェクトに対してインテント フィルタを指定できるのと同様に、Wearable API を通じてライブリスナーを登録する際にもインテント フィルタを使用できます。API ベースのライブリスナーとマニフェスト ベースのリスナーの両方に同じルールが適用されます。
一般的なパターンとしては、LifecycleObserver を使用して特定のパスまたはパス プレフィックスを指定してリスナーを登録します。この方法でリスナーを実装すると、アプリが選択的にイベントを受信できるようになるため、アプリの設計と効率性を改善することができます。