Wear에서 데이터 영역 이벤트 처리

Data Layer API를 호출하면 완료 시 호출의 상태를 수신할 수 있습니다. 또한 Google 네트워크의 Wear OS에서 앱이 만드는 데이터 변경으로 인해 발생하는 데이터 이벤트를 수신 대기할 수 있습니다.

참고: Wear 앱은 Data Layer API를 사용하여 스마트폰 앱과 통신할 수 있지만, 이 API를 사용하여 네트워크에 연결하는 것은 권장되지 않습니다.

다음의 관련 리소스를 참조하세요.

데이터 영역 호출의 상태 대기

Data Layer API를 호출하면(예: DataClient 클래스의 putDataItem 메서드를 사용한 호출) 때때로 Task<ResultType> 객체가 반환됩니다. Task 객체가 생성되자마자 작업은 백그라운드에서 대기합니다. 이후에 아무것도 하지 않으면 작업이 결국 자동으로 완료됩니다. 그러나 일반적으로 작업이 완료된 후 결과로 무언가를 수행할 수 있으므로, Task 객체를 사용하면 결과 상태를 대기(동기 또는 비동기)할 수 있습니다.

비동기 호출

기본 UI 스레드에서 코드를 실행 중인 경우 Data Layer API에 관한 호출을 차단하지 마세요. 작업 완료 시 실행되는 Task 객체에 콜백 메서드를 추가하여 비동기로 호출을 실행할 수 있습니다.

Kotlin

    // Using Kotlin function references
    task.addOnSuccessListener(::handleDataItem)
    task.addOnFailureListener(::handleDataItemError)
    task.addOnCompleteListener(::handleTaskComplete)
    ...
    fun handleDataItem(dataItem: DataItem) { ... }
    fun handleDataItemError(exception: Exception) { ... }
    fun handleTaskComplete(task: Task<DataItem>) { ... }
    

자바

    // Using Java 8 Lambdas.
    task.addOnSuccessListener(dataItem -> handleDataItem(dataItem));
    task.addOnFailureListener(exception -> handleDataItemError(exception));
    task.addOnCompleteListener(task -> handleTaskComplete(task));
    

서로 다른 작업의 실행을 함께 연결하는 기능을 포함하여 다른 가능성을 알아보려면 Task API를 참조하세요.

동기 호출

코드가 백그라운드 서비스의 별도 핸들러 스레드에서 실행되는 경우( WearableListenerService의 경우), 호출을 차단해도 괜찮습니다. 이 경우 Task 객체에서 Tasks.await()를 호출할 수 있습니다. 이 호출은 요청이 완료되어 Result 객체를 반환할 때까지 차단됩니다.

Kotlin

    try {
        Tasks.await(dataItemTask).apply {
            Log.d(TAG, "Data item set: $uri")
        }
    }
    catch (e: ExecutionException) { ... }
    catch (e: InterruptedException) { ... }
    

자바

    try {
        DataItem item = Tasks.await(dataItemTask);
        Log.d(TAG, "Data item set: " + item.getUri());
    } catch (ExecutionException | InterruptedException e) {
      ...
    }
    

데이터 영역 이벤트 수신 대기

데이터 영역은 핸드헬드와 웨어러블 전체에서 데이터를 동기화 및 전송하기 때문에 일반적으로 중요한 이벤트를 수신 대기해야 할 필요가 있습니다. 이러한 이벤트의 예로는 데이터 항목 생성 및 메시지 수신이 있습니다.

데이터 영역 이벤트를 수신 대기하기 위한 두 가지 옵션이 있습니다.

이 두 옵션을 사용하여 처리하고자 하는 이벤트에 관한 데이터 이벤트 콜백 메서드를 재정의합니다.

참고: 배터리 사용량과 관련하여 WearableListenerService는 앱의 manifest에 등록되며, 앱이 아직 실행되고 있지 않은 경우 앱을 시작할 수 있습니다. 앱이 이미 실행 중일 때에만 이벤트를 수신 대기해야 하는 경우(대화형 애플리케이션에서 일반적임) WearableListenerService를 사용하지 마세요. 대신 라이브 리스너를 등록하세요. (이때 예를 들어 DataClient 클래스의 addListener 메서드를 사용합니다.) 이렇게 하면 시스템의 부하를 줄이고 배터리 사용량도 줄일 수 있습니다.

WearableListenerService 사용

일반적으로 웨어러블 및 핸드헬드 앱에서 모두 이 서비스의 인스턴스를 만듭니다. 이러한 앱 중 하나의 데이터 이벤트에 관심이 없다면 특정 앱에서 이 서비스를 구현할 필요가 없습니다.

예를 들어, 데이터 항목 객체를 설정하고 가져오는 핸드헬드 앱 및 UI 업데이트를 위해 해당 업데이트를 수신 대기하는 웨어러블 앱이 있다고 가정해 보겠습니다. 웨어러블은 데이터 항목을 업데이트하지 않으므로 핸드헬드 앱은 웨어러블 앱에서 데이터 이벤트를 수신 대기하지 않습니다.

WearableListenerService를 사용하여 수신 대기할 수 있는 몇몇 이벤트는 다음과 같습니다.

  • onDataChanged(): 데이터 항목 객체가 생성, 삭제 또는 변경될 때마다 시스템은 연결된 모든 노드에서 이 콜백을 트리거합니다.
  • onMessageReceived(): 노드에서 전송된 메시지가 타겟 노드에서 이 콜백을 트리거합니다.
  • onCapabilityChanged(): 앱의 인스턴스가 알리는 기능이 네트워크에서 사용 가능해지면 해당 이벤트가 이 콜백을 트리거합니다. 근처 노드를 찾고 있다면 이 콜백에서 제공된 노드의 isNearby() 메서드를 쿼리할 수 있습니다.

이 목록에 있는 것 외에도 ChannelClient.ChannelCallback에서 onChannelOpened()와 같은 이벤트를 수신 대기할 수 있습니다.

위의 모든 이벤트는 백그라운드 스레드에서 실행되며 주 스레드에서는 실행되지 않습니다.

WearableListenerService를 만들려면 다음 단계를 따르세요.

  1. WearableListenerService를 확장하는 클래스를 만듭니다.
  2. 관심이 있는 이벤트(예: onDataChanged())를 수신 대기합니다.
  3. WearableListenerService에 관해 시스템에 알리기 위해 Android manifest에서 인텐트 필터를 선언합니다. 이렇게 선언하면 시스템은 필요에 따라 서비스를 바인딩할 수 있습니다.

다음 예제는 간단한 WearableListenerService를 구현하는 방법을 보여줍니다.

Kotlin

    private const val TAG = "DataLayerSample"
    private const val START_ACTIVITY_PATH = "/start-activity"
    private const val DATA_ITEM_RECEIVED_PATH = "/data-item-received"

    class DataLayerListenerService : WearableListenerService() {

        override fun onDataChanged(dataEvents: DataEventBuffer) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "onDataChanged: $dataEvents")
            }

            // Loop through the events and send a message
            // to the node that created the data item.
            dataEvents.map { it.dataItem.uri }
                    .forEach { uri ->
                        // Get the node id from the host value of the URI
                        val nodeId: String = uri.host
                        // Set the data of the message to be the bytes of the URI
                        val payload: ByteArray = uri.toString().toByteArray()

                        // Send the RPC
                        Wearable.getMessageClient(this)
                                .sendMessage(nodeId, DATA_ITEM_RECEIVED_PATH, payload)
                    }
        }
    }
    

자바

    public class DataLayerListenerService extends WearableListenerService {
        private static final String TAG = "DataLayerSample";
        private static final String START_ACTIVITY_PATH = "/start-activity";
        private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";

        @Override
        public void onDataChanged(DataEventBuffer dataEvents) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "onDataChanged: " + dataEvents);
            }

            // Loop through the events and send a message
            // to the node that created the data item.
            for (DataEvent event : dataEvents) {
                Uri uri = event.getDataItem().getUri();

                // Get the node id from the host value of the URI
                String nodeId = uri.getHost();
                // Set the data of the message to be the bytes of the URI
                byte[] payload = uri.toString().getBytes();

                // Send the RPC
                Wearable.getMessageClient(this).sendMessage(
                      nodeId,  DATA_ITEM_RECEIVED_PATH, payload);
            }
        }
    }
    

다음 섹션에서는 인텐트 필터를 이 리스너와 함께 사용하는 방법을 설명합니다.

WearableListenerService와 함께 필터 사용

이전 섹션의 WearableListenerService 예제에 관한 인텐트 필터는 다음과 같을 수 있습니다.

    <service android:name=".DataLayerListenerService">
      <intent-filter>
          <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
          <data android:scheme="wear" android:host="*"
                   android:path="/start-activity" />
      </intent-filter>
    </service>
    

이 필터에서는 DATA_CHANGED 작업이 앞서 권장된 BIND_LISTENER 작업을 교체하므로, 특정 이벤트만이 앱을 시작하거나 앱의 절전 모드를 해제합니다. 이렇게 변경하면 시스템 효율성이 향상되고 배터리 소모 및 앱과 관련된 기타 오버헤드가 줄어듭니다. 이 예제에서 시계는 /start-activity 데이터 항목을 수신 대기하고 스마트폰은 /data-item-received 메시지 응답을 수신 대기합니다.

표준 Android 필터 매칭 규칙이 적용됩니다. manifest당 여러 서비스, 서비스당 여러 인텐트 필터, 필터당 여러 작업, 필터당 여러 데이터 스탠자를 지정할 수 있습니다. 와일드카드 호스트 또는 특정 호스트에서 필터를 매칭할 수 있습니다. 와일드카드 호스트에서 매칭하려면 host="*"를 사용하고, 특정 호스트에서 매칭하려면 host=<node_id>를 지정하세요.

리터럴 경로 또는 경로 접두어를 매칭할 수도 있습니다. 경로 또는 경로 접두어로 매칭하는 경우 와일드카드 또는 특정 호스트를 지정해야 합니다. 그렇게 하지 않으면 시스템은 지정된 경로를 무시합니다.

Wear가 지원하는 필터 유형에 관한 자세한 내용은 WearableListenerService에 관한 API 참조 설명서를 참조하세요.

데이터 필터 및 매칭 규칙에 관한 자세한 내용은 data manifest 요소에 관한 API 참조 설명서를 참조하세요.

인텐트 필터를 매칭할 때 기억해야 할 두 가지 중요한 규칙이 있습니다.

  • 인텐트 필터의 스키마가 지정되어 있지 않으면 시스템은 다른 모든 URI 속성을 무시합니다.
  • 필터에 호스트가 지정되어 있지 않으면 시스템은 모든 경로 속성을 무시합니다.

라이브 리스너 사용

사용자가 앱과 상호작용할 때 앱이 데이터 영역 이벤트에만 관심을 두는 경우 모든 데이터 변경사항을 처리하기 위한 장기 실행 서비스가 필요하지 않을 수 있습니다. 그러한 경우 다음 인터페이스 중 하나 이상을 구현하여 활동의 이벤트를 수신 대기할 수 있습니다.

데이터 이벤트를 수신 대기하는 활동을 만들려면 다음을 따르세요.

  1. 원하는 인터페이스를 구현합니다.
  2. onCreate() 또는 onResume() 메서드에서 Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener() 또는 ChannelClient.registerChannelCallback()을 호출하여, 활동이 데이터 영역 이벤트 수신 대기에 관심이 있음을 Google Play에 알립니다.
  3. onStop() 또는 onPause()에서 DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener() 또는 ChannelClient.unregisterChannelCallback()을 사용하여 리스너의 등록을 취소합니다.
  4. 활동이 특정 경로 접두어가 있는 이벤트에만 관심이 있는 경우, 현재 애플리케이션 상태와 관련이 있는 데이터만 수신하도록 적절한 접두어 필터를 사용하여 리스너를 추가할 수 있습니다.
  5. 구현한 인터페이스에 따라 onDataChanged(), onMessageReceived(), onCapabilityChanged(), 또는 ChannelClient.ChannelCallback의 메서드를 구현합니다. 이러한 메서드는 주 스레드에서 호출됩니다. 또는 WearableOptions를 사용하여 맞춤형 Looper를 지정할 수 있습니다.

다음은 DataClient.OnDataChangedListener를 구현하는 예제입니다.

Kotlin

    class MainActivity : Activity(), DataClient.OnDataChangedListener {

        public override fun onResume() {
            Wearable.getDataClient(this).addListener(this)
        }

        override fun onPause() {
            Wearable.getDataClient(this).removeListener(this)
        }

        override fun onDataChanged(dataEvents: DataEventBuffer) {
            dataEvents.forEach { event ->
                if (event.type == DataEvent.TYPE_DELETED) {
                    Log.d(TAG, "DataItem deleted: " + event.dataItem.uri)
                } else if (event.type == DataEvent.TYPE_CHANGED) {
                    Log.d(TAG, "DataItem changed: " + event.dataItem.uri)
                }
            }
        }
    }
    

자바

    public class MainActivity extends Activity implements DataClient.OnDataChangedListener {

        @Override
        public void onResume() {
            Wearable.getDataClient(this).addListener(this);
        }

        @Override
        protected void onPause() {
            Wearable.getDataClient(this).removeListener(this);
        }

        @Override
        public void onDataChanged(DataEventBuffer dataEvents) {
            for (DataEvent event : dataEvents) {
                if (event.getType() == DataEvent.TYPE_DELETED) {
                    Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
                } else if (event.getType() == DataEvent.TYPE_CHANGED) {
                    Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
                }
            }
        }
    }
    

라이브 리스너와 함께 필터 사용

이 페이지의 앞부분에서 설명했듯이 manifest 기반 WearableListenerService 객체에 관해 인텐트 필터를 지정할 수 있는 것처럼, Wearable API를 통해 라이브 리스너를 등록할 때에도 인텐트 필터를 사용할 수 있습니다. API 기반 라이브 리스너와 manifest 기반 리스너에 모두 동일한 규칙이 적용됩니다.

공통된 패턴은 활동의 onResume() 메서드에서 특정 경로 또는 경로 접두어로 리스너를 등록하고, 활동의 onPause() 메서드에서 리스너를 삭제하는 것입니다. 이러한 방식으로 리스너를 구현하면 앱이 이벤트를 좀 더 선택적으로 수신할 수 있으므로 디자인과 효율성이 개선됩니다.