处理 Wear 上的数据层事件

当您调用 Data Layer API 时,可以在调用完成时收到其状态。您还可以监听因您的应用在 Wear OS by Google 谷歌网络上任何位置做出的数据更改而引发的数据事件。

注意:Wear 应用可以使用 Data Layer API 与手机应用通信,但不建议使用此 API 连接到网络

请参阅以下相关资源:

等待数据层调用的状态

调用 Data Layer API(例如,使用 DataClient 类的 putDataItem 方法进行调用)有时会返回 Task<ResultType> 对象。一旦创建了 Task 对象,操作便会在后台排队。如果您在此之后不再进行任何处理,则操作最终会以静默方式完成。不过,您通常需要在操作完成后对结果进行某种处理,因此 Task 对象允许您以同步或异步方式等待结果状态。

异步调用

如果您的代码在主界面线程上运行,请勿对 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) {
      ...
    }
    

监听数据层事件

由于数据层会在手持式设备和穿戴式设备之间同步和发送数据,因此通常有必要监听重要事件。举例来说,创建数据项和接收消息等便属于此类事件。

要监听数据层事件,您有两个选择:

无论选择哪一个,您都需要替换想要处理的事件的数据事件回调方法。

注意:关于电池用量,在应用的清单中注册了 WearableListenerService,如果应用尚未运行,该服务会将其启动。如果您只需要在应用已在运行时监听事件(交互式应用通常就是如此),那么请不要使用 WearableListenerService,而应注册实时监听器,例如使用 DataClient 类的 addListener 方法进行注册。这样可以减少系统上的负载并减少电池用量。

使用 WearableListenerService

您通常会同时在穿戴式设备应用和手持式设备应用中创建此服务的实例。如果您对其中一个应用中的数据事件不感兴趣,则无需在该特定应用中实现此服务。

例如,您可以让手持式设备应用设置并获取数据项对象,而让穿戴式设备应用监听这些更新以更新其界面。穿戴式设备应用从不更新任何数据项,因此手持式设备应用不会监听任何来自穿戴式设备应用的数据事件。

您可以使用 WearableListenerService 监听的一些事件如下:

  • onDataChanged():每当创建、删除或更改数据项对象时,系统都会在所有连接的节点上触发此回调。
  • onMessageReceived():从某个节点发送的消息会在目标节点上触发此回调。
  • onCapabilityChanged():当您的应用实例广播的某个功能发布到网络上时,该事件会触发此回调。如果您要查找一个附近的节点,可以查询回调中提供的节点的 isNearby() 方法。

除了此列表中的事件之外,您还可以监听来自 ChannelClient.ChannelCallback 的事件,如 onChannelOpened()

上述所有事件都在后台线程上执行,而不是在主线程上执行。

要创建 WearableListenerService,请按以下步骤操作:

  1. 创建一个扩展 WearableListenerService 的类。
  2. 监听您感兴趣的事件,如 onDataChanged()
  3. 在 Android 清单中声明一个 Intent 过滤器,以向系统通知您的 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);
            }
        }
    }
    

下一部分介绍如何将 Intent 过滤器与此监听器一起使用。

对 WearableListenerService 使用过滤器

上一部分中所示 WearableListenerService 示例的 Intent 过滤器可能如下所示:

    <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>
    

在此过滤器中,DATA_CHANGED 操作取代了先前建议的 BIND_LISTENER 操作,使得只有特定事件才会唤醒或启动您的应用。这一变化可提高系统效率,并减少耗电量以及与您的应用有关的其他开销。在本例中,手表监听 /start-activity 数据项,而手机监听 /data-item-received 消息回复。

需遵守标准 Android 过滤器匹配规则。您可以为每个清单指定多项服务,为每项服务指定多个 Intent 过滤器,为每个过滤器指定多项操作,并为每个过滤器指定多个数据 stanza。过滤器可以匹配通配符主机或特定主机。要匹配通配符主机,请使用 host="*"。要匹配特定主机,请指定 host=<node_id>

您还可以匹配字面量路径或路径前缀。如果要按路径或路径前缀进行匹配,您必须指定通配符或特定主机。如果您不这样做,系统会忽略您指定的路径。

如需详细了解 Wear 支持的过滤器类型,请参阅 WearableListenerService 的 API 参考文档。

如需详细了解数据过滤器和匹配规则,请参阅 data 清单元素的 API 参考文档。

匹配 Intent 过滤器时,需要牢记两条重要规则:

  • 如果没有为 Intent 过滤器指定地址协议,系统会忽略其他所有 URI 属性。
  • 如果没有为过滤器指定主机,系统会忽略所有路径属性。

使用实时监听器

如果您的应用只关心用户与应用互动时的数据层事件,则处理每项数据更改可能不需要长时间运行的服务。在这种情况下,您可以通过实现以下一个或多个接口来监听 Activity 中的事件:

要创建用于监听数据事件的 Activity,请执行以下操作:

  1. 实现所需接口。
  2. onCreate()onResume() 方法中,调用 Wearable.getDataClient(this).addListener()MessageClient.addListener()CapabilityClient.addListener()ChannelClient.registerChannelCallback(),以通知 Google Play 服务您的 Activity 想要监听数据层事件。
  3. onStop()onPause() 中,使用 DataClient.removeListener()MessageClient.removeListener()CapabilityClient.removeListener()ChannelClient.unregisterChannelCallback() 取消注册任何监听器。
  4. 如果某个 Activity 仅对具有特定路径前缀的事件感兴趣,您可以添加具有合适前缀过滤器的监听器,以便仅接收与当前应用状态有关的数据。
  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 对象指定 Intent 过滤器一样,您也可以在通过 Wearable API 注册监听器时使用 Intent 过滤器。基于 API 的实时监听器和基于清单的监听器需遵守相同的规则。

一种常见模式是在 Activity 的 onResume() 方法中注册具有特定路径或路径前缀的监听器,并在 Activity 的 onPause() 方法中移除该监听器。以这种方式实现监听器可以让您的应用更有选择性地接收事件,从而改进其设计和效率。