Wear OS のデータを新しいモバイル デバイスに転送する

ユーザーが Wear OS デバイスをセットアップする際には、Wear OS デバイスを特定のモバイル デバイスに接続します。その後、新しいモバイル デバイスを入手して、既存の Wear OS デバイスをその新しいモバイル デバイスに接続する可能性があります。Wear OS デバイスに関連するデータの一部は、その時点で接続中のモバイル デバイスに保存されます。

Wear OS 4 以降では、ユーザーが新しいモバイル デバイスに接続したときに、Wear OS のデータを新しいモバイル デバイスに転送できます。データは転送時に自動的に同期されます。

ユーザーが転送をリクエストすると、Wearable Data Layer によって、一方のモバイル デバイスに保存されていた DataItem オブジェクトがもう一方のモバイル デバイスに送られます。これで、ユーザーはアプリをシームレスに利用できるようになります。

ここでは、このシナリオに対応するよう Wear OS アプリとそのコンパニオン モバイルアプリを設定する方法について説明します。

準備

データ転送プロセスにおける DataItem オブジェクトの扱い方は、どちらのアプリがデータを所有するかによって異なります。

Wear OS アプリが所有するオブジェクト
Wear OS デバイスに保持されます。
モバイルアプリが所有するオブジェクト

以前のデバイスにアーカイブされます。アーカイブされたデータは、システムによって DataItemBuffer オブジェクトにパッケージングされ、新しいモバイル デバイスにインストールされているモバイルアプリに送られます。

アーカイブが送られるとすぐに、Wearable Data Layer から onDataChanged() リスナーが呼び出されます。これは、Wear OS デバイスからデータが書き込まれると通知される仕組みと同様です。

転送されたデータを保持する

転送された DataItem オブジェクトは、アプリ側で保持してください。データが新しいモバイル デバイスに送られるとすぐに、以前のデバイスからアーカイブが削除されます。

次の各条件を満たしていることを確認してください。

  1. 転送に関与するどちらのモバイル デバイスにもアプリがインストールされていること。
  2. それぞれのモバイル デバイスにインストールされているモバイルアプリのパッケージ署名が一致すること。

上記の条件を満たさないと、アーカイブされた DataItem オブジェクトは送信されず、破棄されます。

以前のモバイル デバイスからデータを受信する

以前のモバイル デバイスにアーカイブされたデータを新しいモバイル デバイスで受信するには、モバイルアプリで、WearableListenerService クラスの onNodeMigrated() コールバックを実装する必要があります。そのための手順は次のとおりです。

  1. モバイルアプリのビルドファイルに、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();
        }
    }