从 11.8.0 版的 Google Play 服务开始,Wear OS 应用应停止使用 GoogleApiClient
类,改为使用基于
GoogleApi
类的客户端对象。
使用
GoogleApi
可以简化异步操作的设置。例如,如 Tasks API 简介中所述,您可以获取 Task
对象,而不是
PendingResult
对象。
本页包含:
- 替换组件表
- 更新现有应用以使用 Tasks API 的示例
注意:这项更新不适用于中国版 Wear OS 应用,这类应用通常使用 10.2.0 版本的 Google Play 服务。
注意:此 API 目前仅适用于 Android 手机以及与 Android 手机配对的 Wear OS 手表。对于与 iOS 手机配对的 Wear OS 手表,在有互联网连接的情况下,应用可以查询其他云端 API。
已废弃组件的替换组件
当您使用扩展
GoogleApi
类的类(如
DataClient
和
MessageClient
)时,Google Play 服务 SDK 会为您管理与 Google Play 服务的连接。
使用以下替换类的应用无需创建和管理 GoogleApiClient
对象。另请参阅访问 Google API 和 Wearable 类的参考页面。
下表包含已废弃的组件及其替换组件:
已废弃组件 | 替换组件 |
CapabilityApi
|
CapabilityClient
|
Channel
|
ChannelClient.Channel
|
ChannelApi
|
ChannelClient
|
DataApi
|
DataClient
|
MessageApi
|
MessageClient
|
NodeApi
|
NodeClient
|
此外,还请注意以下事项:
- 对于有关频道更改的通知,
Channel.ChannelListener
已被ChannelClient.ChannelCallback
取代 - 对于监听器回调的线程设置,
GoogleApiClient.Builder.setHandler
已被WearableOptions.Builder
的setLooper
方法取代
Wear 应用的迁移示例
作为迁移示例,以下代码段说明了 Wear 数据层示例(使用了 Data Layer API )针对 11.8.0 版本的 Google Play 服务进行了怎样的更新。如果您的应用具有手机模块,其更新可能与 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 ...
Java
... 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 数据层示例的主 activity(如 GitHub 上的 diff 中所示)实现了 CapabilityApi.CapabilityListener
接口。但现在,主 activity 实现的是 CapabilityClient.OnCapabilityChangedListener
。
下面对类定义进行了比较。
以下是使用 11.8.0 版 Google Play 服务之前的代码段:
Kotlin
class MainActivity : Activity(), GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
Java
public class MainActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
以下是使用 11.8.0 版 Google Play 服务之后的代码段:
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 请求信息
如果数据有变化,您可能需要在可更新应用的监听器之外请求信息。在这种情况下,请将
DataClient
之类的客户端与 Tasks API 和结果类(即 Task<ResultType>
)结合使用来发出请求。
例如,如 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() }
Java
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 和 Tasks API 的其他代码,请参阅 Wear 数据层示例。以在界面线程或服务中阻塞需要大量资源的任务为例,还有另外一种选择。以下示例展示了如何阻塞相关任务并同步获得结果:
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); } }