Google Play 서비스 버전 11.8.0부터 Wear OS 앱은 GoogleApiClient
클래스에서 이전하고 대신
GoogleApi
클래스를 기반으로 한 클라이언트 객체를 사용해야 합니다.
GoogleApi
를 사용하면 비동기 작업을 더 쉽게 설정할 수 있습니다.
예를 들어 Tasks API 소개에 설명된 것처럼
PendingResult
객체 대신 Task
객체를 얻을 수 있습니다.
이 페이지에는 다음이 포함되어 있습니다.
- 교체용 구성요소 표
- Tasks API 사용을 위해 기존 앱을 업데이트하는 방법의 예
참고: 이 업데이트는 일반적으로 Google Play 서비스 버전 10.2.0을 사용하는 중국용 Wear OS 앱에는 적용되지 않습니다.
참고: 이 API는 현재 Android 휴대전화 및 Android 휴대전화와 페어링된 Wear OS 시계에서만 사용할 수 있습니다. iOS 휴대전화와 페어링된 Wear OS 시계의 경우 인터넷에 연결할 수 있다면 앱에서는 다른 클라우드 기반 API를 쿼리할 수 있습니다.
지원 중단된 구성요소 교체
DataClient
및
MessageClient
와 같이
GoogleApi
클래스를 확장하는 클래스를 사용하면 Google Play 서비스 SDK가 Google Play 서비스 연결을 자동으로 관리합니다.
아래의 교체용 클래스를 사용하는 앱은 GoogleApiClient
객체를 만들고 관리할 필요가 없습니다. Google API 액세스 및 웨어러블 클래스의 참조 페이지도 확인하세요.
아래 표에는 지원 중단된 구성요소와 교체용 구성요소가 나와 있습니다.
지원 중단된 구성요소 | 교체용 구성요소 |
CapabilityApi
|
CapabilityClient
|
Channel
|
ChannelClient.Channel
|
ChannelApi
|
ChannelClient
|
DataApi
|
DataClient
|
MessageApi
|
MessageClient
|
NodeApi
|
NodeClient
|
또한 다음을 참고하세요.
- 채널 변경 알림을 위해
Channel.ChannelListener
가ChannelClient.ChannelCallback
으로 교체됨 - 리스너 콜백의 스레드를 설정하기 위해
GoogleApiClient.Builder.setHandler
가WearableOptions.Builder
의setLooper
메서드로 교체됨
Wear 앱의 이전 예
이전 예로 제공되는 아래 코드 스니펫에서는 Data Layer API를 사용하는 Wear 데이터 영역 샘플이 Google Play 서비스 버전 11.8.0용으로 어떻게 업데이트되었는지를 보여줍니다. 휴대전화 모듈이 있는 앱인 경우 업데이트가 Wear 모듈 업데이트와 비슷할 수 있습니다.
Google Play 서비스 종속 항목 업데이트
앱이 이전 버전의 Google Play 서비스를 사용할 수 있으므로 Wear 모듈의 build.gradle
파일에서 다음 종속 항목을 업데이트합니다.
dependencies { ... compile 'com.google.android.gms:play-services-wearable:11.8.0' }
앱의 import 문 업데이트
Tasks API의 클래스를 포함하여 필요한 클래스를 가져옵니다.
예를 들어 이전에는 Wear 데이터 영역 샘플의 MainActivity.java
파일에 다음 import 문이 포함되었습니다. 이 import
문은 삭제해야 합니다.
Kotlin
... import com.google.android.gms.common.api.GoogleApiClient ...
자바
... import com.google.android.gms.common.api.GoogleApiClient; ...
Wear 데이터 영역 샘플에서 위와 같은 import
문은 다음과 같이 바뀌었습니다(두 번째는 작업 예외 처리를 위한 문임).
Kotlin
... import com.google.android.gms.tasks.Tasks import java.util.concurrent.ExecutionException ...
Java
... import com.google.android.gms.tasks.Tasks; import java.util.concurrent.ExecutionException; ...
새 클라이언트 인터페이스 구현
사용된 모든 GoogleApiClient
클래스와 관련 인터페이스(ConnectionCallbacks
, OnConnectionFailedListener
등)를 삭제하고 그 밖의 구현된 리스너를 새 버전으로 바꿉니다. 재정의할 실제 메서드에 사용되는 이름은 이전과 동일하므로 주요 변화는 아래에 나와 있는 예와 유사합니다.
Wear 데이터 영역 샘플의 기본 활동(GitHub의 diff에 표시된 것처럼)은 CapabilityApi.CapabilityListener
같은 인터페이스를 구현했습니다. 그러나 이제 기본 활동은 CapabilityClient.OnCapabilityChangedListener
를 구현합니다.
다음은 클래스 정의를 비교한 것입니다.
아래는 Google Play 서비스 버전 11.8.0 사용 전의 스니펫입니다.
Kotlin
class MainActivity : Activity(), GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
자바
public class MainActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
아래는 Google Play 서비스 버전 11.8.0 사용 후의 스니펫입니다.
Kotlin
class MainActivity : Activity(), DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
Java
public class MainActivity extends Activity implements DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
리스너 삭제 및 추가
새 클라이언트 객체는 캐시되고
GoogleApi
인스턴스 간에 공유되므로 멤버 변수를 보관할 필요가 없습니다. 클라이언트는 저렴한 비용으로 만들 수 있으며 리스너를 놓치지 않습니다.
다음은 수정된 Wear 데이터 영역 샘플의 스니펫입니다.
Kotlin
override fun onResume() { super.onResume() Wearable.getDataClient(this).addListener(this) Wearable.getMessageClient(this).addListener(this) Wearable.getCapabilityClient(this) .addListener( this, Uri.parse("wear://"), CapabilityClient.FILTER_REACHABLE ) } override fun onPause() { super.onPause() Wearable.getDataClient(this).removeListener(this) Wearable.getMessageClient(this).removeListener(this) Wearable.getCapabilityClient(this).removeListener(this) }
Java
@Override protected void onResume() { super.onResume(); Wearable.getDataClient(this).addListener(this); Wearable.getMessageClient(this).addListener(this); Wearable.getCapabilityClient(this) .addListener( this, Uri.parse("wear://"), CapabilityClient.FILTER_REACHABLE); } @Override protected void onPause() { super.onPause(); Wearable.getDataClient(this).removeListener(this); Wearable.getMessageClient(this).removeListener(this); Wearable.getCapabilityClient(this).removeListener(this); }
Tasks API를 사용하여 정보 요청
데이터 변경이 있을 때 앱을 업데이트하는 리스너의 외부에서 정보를 요청할 수 있습니다. 이러한 경우 Tasks API 및 결과 클래스(예: Task<ResultType>
)와 함께
DataClient
같은 클라이언트를 사용하여 정보를 요청합니다.
예를 들어 Wear 데이터 영역 샘플에서와 같이 Tasks API를 사용하여 지정된 기능과 연결된 노드를 찾을 수 있습니다.
Kotlin
private fun showNodes(vararg capabilityNames: String) { Wearable.getCapabilityClient(this) .getAllCapabilities(CapabilityClient.FILTER_REACHABLE).apply { addOnSuccessListener { capabilityInfoMap -> val nodes: Set<Node> = capabilityInfoMap .filter { capabilityNames.contains(it.key) } .flatMap { it.value.nodes } .toSet() showDiscoveredNodes(nodes) } } } private fun showDiscoveredNodes(nodes: Set<Node>) { val nodesList: Set<String> = nodes.map { it.displayName }.toSet() val msg: String = if (nodesList.isEmpty()) { Log.d(TAG, "Connected Nodes: No connected device was found for the given capabilities") getString(R.string.no_device) } else { Log.d(TAG, "Connected Nodes: ${nodesList.joinToString(separator = ", ")}") getString(R.string.connected_nodes, nodesList) } Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show() }
자바
private void showNodes(final String... capabilityNames) { Task<Map<String, CapabilityInfo>> capabilitiesTask = Wearable.getCapabilityClient(this) .getAllCapabilities(CapabilityClient.FILTER_REACHABLE); capabilitiesTask.addOnSuccessListener(new OnSuccessListener<Map<String, CapabilityInfo>>() { @Override public void onSuccess(Map<String, CapabilityInfo> capabilityInfoMap) { Set<Node> nodes = new HashSet<>(); if (capabilityInfoMap.isEmpty()) { showDiscoveredNodes(nodes); return; } for (String capabilityName : capabilityNames) { CapabilityInfo capabilityInfo = capabilityInfoMap.get(capabilityName); if (capabilityInfo != null) { nodes.addAll(capabilityInfo.getNodes()); } } showDiscoveredNodes(nodes); } }); } private void showDiscoveredNodes(Set<Node> nodes) { List<String> nodesList = new ArrayList<>(); for (Node node : nodes) { nodesList.add(node.getDisplayName()); } LOGD(TAG, "Connected Nodes: " + (nodesList.isEmpty() ? "No connected device was found for the given capabilities" : TextUtils.join(",", nodesList))); String msg; if (!nodesList.isEmpty()) { msg = getString(R.string.connected_nodes, TextUtils.join(", ", nodesList)); } else { msg = getString(R.string.no_device); } Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show(); }
Wearable API와 Tasks API를 활용하는 다른 코드를 보려면 Wear 데이터 영역 샘플을 참고하세요. UI 스레드 또는 서비스의 작업을 과도하게 사용하는 경우 다른 옵션을 이용할 수 있습니다. 다음은 작업을 차단하고 동기적으로 결과를 얻는 방법의 예입니다.
Kotlin
override fun doInBackground(vararg params: Asset): Bitmap? { if (params.isNotEmpty()) { val asset = params[0] val getFdForAssetResponseTask: Task<DataClient.GetFdForAssetResponse> = Wearable.getDataClient(applicationContext).getFdForAsset(asset) return try { // Block on a task and get the result synchronously. This is generally done // when executing a task inside a separately managed background thread. Doing // this on the main (UI) thread can cause your application to become // unresponsive. val getFdForAssetResponse: DataClient.GetFdForAssetResponse = Tasks.await(getFdForAssetResponseTask) getFdForAssetResponse.inputStream?.let { assetInputStream -> BitmapFactory.decodeStream(assetInputStream) } ?: run { Log.w(TAG, "Requested an unknown Asset.") null } } catch (exception: ExecutionException) { Log.e(TAG, "Failed retrieving asset, Task failed: $exception") return null } catch (exception: InterruptedException) { Log.e(TAG, "Failed retrieving asset, interrupt occurred: $exception") return null } } else { Log.e(TAG, "Asset must be non-null") return null } } override fun onPostExecute(bitmap: Bitmap?) { bitmap?.also { Log.d(TAG, "Setting background image on second page..") moveToPage(1) assetFragment.setBackgroundImage(it) } }
Java
@Override protected Bitmap doInBackground(Asset... params) { if (params.length > 0) { Asset asset = params[0]; Task<DataClient.GetFdForAssetResponse> getFdForAssetResponseTask = Wearable.getDataClient(getApplicationContext()).getFdForAsset(asset); try { // Block on a task and get the result synchronously. This is generally done // when executing a task inside a separately managed background thread. Doing // this on the main (UI) thread can cause your application to become // unresponsive. DataClient.GetFdForAssetResponse getFdForAssetResponse = Tasks.await(getFdForAssetResponseTask); InputStream assetInputStream = getFdForAssetResponse.getInputStream(); if (assetInputStream != null) { return BitmapFactory.decodeStream(assetInputStream); } else { Log.w(TAG, "Requested an unknown Asset."); return null; } } catch (ExecutionException exception) { Log.e(TAG, "Failed retrieving asset, Task failed: " + exception); return null; } catch (InterruptedException exception) { Log.e(TAG, "Failed retrieving asset, interrupt occurred: " + exception); return null; } } else { Log.e(TAG, "Asset must be non-null"); return null; } } @Override protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { LOGD(TAG, "Setting background image on second page.."); moveToPage(1); assetFragment.setBackgroundImage(bitmap); } }