將 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 會立即叫用 onNodeMigrated() 事件監聽器,這類似於系統在 Wear OS 裝置寫入資料時通知應用程式的方式。

保留已轉移的資料

應用程式有責任保留轉移的 DataItem 物件。資料傳送到新行動裝置後不久,系統就會刪除舊裝置上的封存資料。

請確認符合下列各項條件:

  1. 轉移作業中的兩部行動裝置都已安裝您的應用程式。
  2. 兩部行動裝置上安裝的行動應用程式都有相符的套件簽名。

如果不符合上述條件,系統不會傳送已封存的 DataItem 物件,而會捨棄物件。

接收舊行動裝置的資料

如要在新行動裝置上接收舊行動裝置的封存資料,行動應用程式必須實作 onNodeMigrated() 回呼,該回呼是 WearableListenerService 類別的一部分。如要這樣做,請完成下列步驟:

  1. 在行動應用程式的建構檔案中,加入 Google Play 服務中最新版穿戴式裝置程式庫的依附元件:

    dependencies {
        ...
        implementation 'com.google.android.gms:play-services-wearable:18.2.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 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())
                }
            }
    
            // Callback stops automatically after 20 seconds of data processing.
            // If you think you need more time, delegate to a coroutine or thread.
            runBlocking {
                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 Task<DataItem> 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;
                return 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());
                }
            }
    
            for (dataItem in dataItemsToHandle) {
                handleDataItem(nodeId, dataItem);
            }
    
            // Callback stops automatically after 20 seconds of data processing.
            // If you think you need more time, delegate to another thread.
        }
    }