데이터 동기화

이 문서에서는 Wear OS 기기와 휴대전화 간에 데이터를 동기화하는 방법을 설명합니다. Data Layer API를 사용해야 하는 경우와 인프라를 사용해야 하는 경우에 관한 개요 가이드를 참고하세요.

네트워크에서 직접 데이터 전송 및 동기화

네트워크와 직접 통신하는 Wear OS 앱을 빌드합니다. 모바일 개발에서 사용하는 것과 동일한 API를 사용하지만 몇 가지 Wear OS만의 차이점을 염두에 두어야 합니다.

Wear OS Data Layer API를 사용하여 데이터 동기화

DataClient는 구성요소가 DataItem 또는 Asset을 읽거나 쓸 수 있도록 API를 노출합니다.

기기에 연결되지 않은 상태에서 데이터 항목과 애셋을 설정할 수 있습니다. 기기가 네트워크 연결을 설정하면 동기화됩니다. 이 데이터는 앱에 비공개이며 다른 기기의 앱에서만 액세스할 수 있습니다.

  • DataItem은 Wear OS 네트워크의 모든 기기에서 동기화됩니다. 일반적으로 크기가 작습니다.

  • Asset을 사용하여 이미지와 같은 대형 객체를 전송합니다. 시스템은 이미 전송된 애셋을 추적하고 중복 삭제를 자동으로 실행합니다.

서비스에서 이벤트 수신 대기

WearableListenerService 클래스를 확장합니다. 시스템은 기본 WearableListenerService의 수명 주기를 관리하며, 데이터 항목이나 메시지를 전송해야 하면 서비스에 바인딩하고 필요한 작업이 없으면 서비스 바인딩을 해제합니다.

활동에서 이벤트 수신 대기

OnDataChangedListener 인터페이스를 구현합니다. 사용자가 앱을 적극적으로 사용하는 경우에만 변경사항을 수신 대기하려면 WearableListenerService 대신 이 인터페이스를 사용하세요.

설명: Data Layer API의 애셋을 사용하여 Android 휴대전화와 Wear OS 시계 간에 이미지와 같은 대형 바이너리 객체를 전송합니다. keywords_public: Wear OS, Data Layer API, 애셋, 블루투스 데이터 전송, 데이터 동기화, DataMap, PutDataRequest

데이터 동기화

블루투스 전송을 통해 다른 기기의 음성 녹음 파일과 같은 대형 바이너리 객체를 공유하려면 Asset을 데이터 항목에 연결하고 이 데이터 항목을 복제된 데이터 스토어에 넣으면 됩니다. 하지만 교환이 연결된 두 기기 간의 일회성 교환인 경우, 더 간단한 직접 전송이 더 적합한지 고려해 보세요.

참고: Data Layer API는 Android를 실행하는 휴대전화 또는 Wear OS 시계에서만 메시지를 전송하고 데이터를 동기화할 수 있습니다. Wear OS 기기가 iOS 기기와 페어링된 경우 Data Layer API는 작동하지 않습니다.

따라서 네트워크와 통신하는 기본적인 방법으로 Data Layer API를 사용하지 마세요. 대신 Wear OS의 네트워크 액세스 및 동기화에 설명된 대로 Wear OS 앱에서 휴대전화 앱과 동일한 패턴(약간의 차이는 있음)을 따르세요.

재전송을 방지하고 블루투스 대역폭을 보존하기 위해 애셋은 데이터 캐싱을 자동으로 처리합니다. 일반적인 패턴은 휴대전화 앱이 이미지를 다운로드하고, 시계에 표시하기 위한 적절한 크기로 축소한 다음, 시계 앱에 애셋으로 공유하는 것입니다. 다음 예는 이 패턴을 보여줍니다.

애셋 전송

애셋을 Asset 클래스의 create...() 메서드 중 하나를 사용하여 만듭니다. 비트맵을 바이트 배열로 변환한 후 애셋을 만들려면 다음 샘플과 같이 createFromBytes()를 호출합니다.

private fun createAssetFromBitmap(bitmap: Bitmap): Asset =
    ByteArrayOutputStream().let { byteStream ->
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream)
        Asset.createFromBytes(byteStream.toByteArray())
    }

이제 putAsset() 메서드를 사용하여 DataMap 또는 PutDataRequest에서 데이터 항목에 애셋을 연결합니다. 그런 다음 다음 샘플과 같이 putDataItem() 메서드를 사용하여 데이터 항목을 데이터 스토어에 넣습니다.

다음 샘플에서는 PutDataRequest를 사용합니다.

private fun Context.sendImagePutDataRequest(): Task<DataItem> {

    val asset: Asset = createAssetFromBitmap(BitmapFactory.decodeResource(resources, R.drawable.ic_walk))
    val request: PutDataRequest = PutDataRequest.create("/image").apply {
        putAsset("profileImage", asset)
    }
    val putTask: Task<DataItem> = Wearable.getDataClient(this).putDataItem(request)

    return putTask
}

다음 샘플에서는 PutDataMapRequest를 사용합니다.

private fun Context.sendImagePutDataMapRequest(): Task<DataItem> {

    val asset: Asset = createAssetFromBitmap(BitmapFactory.decodeResource(resources, R.drawable.ic_walk))
    val request: PutDataRequest = PutDataMapRequest.create("/image").run {
        dataMap.putAsset("profileImage", asset)
        asPutDataRequest()
    }
    val putTask: Task<DataItem> = Wearable.getDataClient(this).putDataItem(request)

    return putTask
}

애셋 수신

애셋을 만든 후 일반적으로 연결의 다른 쪽에서 애셋을 읽고 추출합니다. 다음은 애셋 변경을 감지하고 애셋을 추출하기 위해 콜백을 구현하는 방법의 예입니다.

override fun onDataChanged(dataEvents: DataEventBuffer) {
    dataEvents
        .filter { it.type == DataEvent.TYPE_CHANGED && it.dataItem.uri.path == "/image" }
        .forEach { event ->
            val asset = DataMapItem.fromDataItem(event.dataItem)
                .dataMap.getAsset("profileImage")

            asset?.let { safeAsset ->
                lifecycleScope.launch {
                    val bitmap = loadBitmapFromAsset(safeAsset)
                    // Do something with the bitmap
                }
            }
        }
}

private suspend fun loadBitmapFromAsset(asset: Asset): Bitmap? = withContext(Dispatchers.IO) {
    try {
        val assetResult = Wearable.getDataClient(this@DataLayerActivity2)
            .getFdForAsset(asset)
            .await()

        assetResult?.inputStream?.use { inputStream ->
            BitmapFactory.decodeStream(inputStream)
        }
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

자세한 내용은 GitHub의 DataLayer 샘플 프로젝트를 참고하세요.