Wear OS 데이터를 새 휴대기기로 전송

사용자는 Wear OS 기기를 설정할 때 Wear OS 기기를 특정 휴대기기에 연결합니다. 사용자는 나중에 새 휴대기기를 구입하고 기존 Wear OS 기기를 이 새 휴대기기에 연결하기로 결정할 수도 있습니다. Wear OS 기기와 관련된 일부 데이터는 현재 연결된 휴대기기에 저장됩니다.

Wear OS 4부터는 사용자가 새 휴대기기에 연결할 때 Wear OS 데이터를 새 휴대기기로 전송할 수 있습니다. 데이터가 전송되면 자동으로 동기화됩니다.

사용자가 전송을 요청하면 웨어러블 데이터 영역이 하나의 휴대기기에 저장되어 있던 DataItem 객체를 다른 휴대기기로 전송합니다. 이를 통해 앱 사용자에게 원활한 환경을 제공할 수 있습니다.

이 문서에서는 이 시나리오를 지원하기 위해 Wear OS 앱 및 호환 모바일 앱을 구성하는 방법을 설명합니다.

준비

데이터 전송 프로세스는 데이터를 소유한 앱에 따라 DataItem 객체를 다르게 처리합니다.

Wear OS 앱이 소유한 객체
이러한 객체는 Wear OS 기기에 보존됩니다.
모바일 앱에서 소유한 객체

이러한 객체는 이전 기기에 보관처리됩니다. 그런 다음, 시스템은 보관처리된 데이터를 DataItemBuffer 객체로 패키징하고 이 데이터를 새 휴대기기에 설치된 모바일 앱으로 전달합니다.

보관 파일이 전달된 직후, 웨어러블 데이터 영역은 onNodeMigrated() 리스너를 호출합니다. Wear OS 기기에서 데이터를 쓸 때 앱에 알림을 보내는 방식과 유사합니다.

전송된 데이터 보존

전송된 DataItem 객체를 보존하는 것은 앱의 책임입니다. 데이터가 새 휴대기기로 전송되면 곧바로 보관 파일이 이전 기기에서 삭제됩니다.

다음 조건이 모두 충족되는지 확인하세요.

  1. 앱이 전송에 관여하는 두 휴대기기에 모두 설치되어 있습니다.
  2. 각 휴대기기에 설치된 모바일 앱의 패키지 서명이 일치합니다.

일치하지 않으면 보관처리된 DataItem 객체가 전달되지 않고 삭제됩니다.

이전 휴대기기에서 데이터 수신

이전 휴대기기에 보관처리된 데이터를 새 휴대기기에서 수신하려면 모바일 앱에서 WearableListenerService 클래스의 일부인 onNodeMigrated() 콜백을 구현해야 합니다. 그러려면 다음 단계를 완료하세요.

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

    자바

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