将 Wear OS 数据传输到新的移动设备

当用户设置 Wear OS 设备时,他们会将 Wear OS 设备连接到特定的移动设备。用户稍后可能决定使用新的移动设备并将其现有的 Wear OS 设备连接到此新移动设备。与 Wear OS 设备相关的一些数据存储在当前连接的移动设备上。

从 Wear OS 4 开始,当用户连接到新的移动设备时,可以将 Wear OS 数据传输到新的移动设备。数据在传输后会自动同步。

用户请求传输时,穿戴式设备数据层会将最初存储在一部移动设备上的 DataItem 对象传递到另一部移动设备。这样可以为您的应用用户提供顺畅的体验。

本文档介绍了如何配置 Wear OS 应用及其配套移动应用来支持这种情形。

准备

数据传输进程处理各个 DataItem 对象的方式有所不同,具体取决于拥有相应数据的应用:

Wear OS 应用拥有的对象
这些对象会保留在 Wear OS 设备上。
移动应用拥有的对象

这些对象会在旧设备上归档。然后,系统会将归档的数据打包到 DataItemBuffer 对象中,并将该数据传递到安装在新移动设备上的移动应用。

在归档文件提交后,穿戴式设备数据层会立即调用 onDataChanged() 监听器,类似于 Wear OS 设备写入数据时应用会如何收到通知。

保留传输的数据

您的应用负责保留传输的 DataItem 对象。在数据传送到新的移动设备后,归档文件很快就会从旧设备上删除。

请确保满足以下每个条件:

  1. 传输数据所涉及的两个移动设备上都安装了您的应用。
  2. 两个移动设备上安装的移动应用具有一致的软件包签名。

否则,系统不会传递已归档的 DataItem 对象,而是会将其舍弃。

从旧移动设备接收数据

如需在旧移动设备上归档的新移动设备上接收数据,您的移动应用必须实现 WearableListenerService 类中的 onNodeMigrated() 回调。为此,请完成以下步骤:

  1. 在移动应用的 build 文件中,添加对 Google Play 服务中最新版穿戴式设备库的依赖项:

    dependencies {
        ...
        implementation 'com.google.android.gms:play-services-wearable:18.1.0'
    }
    
  2. 在应用的清单文件中声明并导出 WearableListenerService

    <service
    android:name=".MyWearableListenerService"
    android:exported="true">
    <intent-filter>
        ...
        <action android:name="com.google.android.gms.wearable.NODE_MIGRATED" />
        <data android:scheme="wear" />
    </intent-filter>
    </service>
    
  3. 创建一个扩展 WearableListenerService 并替换 onNodeMigrated() 的服务类。

    Kotlin

    
    class MyWearableListenerService : WearableListenerService() {
        val dataClient: DataClient = Wearable.getDataClient(this)
    
        private fun shouldHandleDataItem(nodeId: String,
                dataItem: DataItem): Boolean {
            // Your logic here
            return dataItem.uri.path?.startsWith("/my_feature_path/") == true
        }
    
        private suspend fun handleDataItem(nodeId: String, dataItem: DataItem) {
            val data = dataItem.data ?: return
            val path = dataItem.uri.path ?: return
            // Your logic here
            if (data.toString().startsWith("Please restore")) {
                dataClient.putDataItem(
                    PutDataRequest.create(path).setData(data))
            }
        }
    
        override fun onNodeMigrated(nodeId: String, archive: DataItemBuffer) {
            val dataItemsToHandle = mutableListOf<DataItem>()
    
            for (dataItem in archive) {
                if (shouldHandleDataItem(nodeId, dataItem)) {
                    dataItemsToHandle.add(dataItem.freeze())
                }
            }
    
            CoroutineScope(Job() + Dispatchers.IO).launch {
                for (dataItem in dataItemsToHandle) {
                    handleDataItem(nodeId, dataItem)
                }
            }
        }
    }
    
    

    Java

    
    public class MyWearableListenerService extends WearableListenerService {
        private final DataClient dataClient = Wearable.getDataClient(this);
    
        private boolean shouldHandleDataItem(String nodeId, DataItem dataItem) {
            // Your logic here
            return Objects.requireNonNull(dataItem.getUri().getPath())
                    .startsWith("/my_feature_path/");
        }
    
        private void handleDataItem(String nodeId, DataItem dataItem) {
            byte[] data = dataItem.getData();
            String path = dataItem.getUri().getPath();
            // Your logic here
            if (data != null && path != null && Arrays.toString(data)
                    .startsWith("Please restore")) {
                assert path != null;
                dataClient.putDataItem(
                        PutDataRequest.create(path).setData(data));
            }
        }
    
        @Override
        public void onNodeMigrated(@NonNull String nodeId, DataItemBuffer archive) {
            List<DataItem> dataItemsToHandle = new ArrayList<>();
    
            for (DataItem dataItem : archive) {
                if (shouldHandleDataItem(nodeId, dataItem)) {
                    dataItemsToHandle.add(dataItem.freeze());
                }
            }
    
            Thread thread = new Thread(() -> {
                for (DataItem dataItem : dataItemsToHandle) {
                    handleDataItem(nodeId, dataItem);
                }
            });
            thread.start();
        }
    }