将 Wear 应用迁移到 GoogleApi

版本 11.8.0 的 Google Play 服务开始,Wear OS 应用应停止使用 GoogleApiClient 类,改为使用基于 GoogleApi 类的客户端对象。

使用 GoogleApi 可以简化异步操作的设置。例如,如 Tasks API 简介中所述,您可以获取 Task 对象,而不是 PendingResult 对象。

本页包含:

  • 替换组件表
  • 更新现有应用以使用 Tasks API 的示例

注意:这项更新不适用于中国版 Wear OS 应用,这类应用通常使用 10.2.0 版 Google Play 服务。

已弃用组件的替换组件

当您使用扩展 GoogleApi 类的类(如 DataClient MessageClient)时,Google Play 服务 SDK 会为您管理与 Google Play 服务的连接。

使用以下替换类的应用无需创建和管理 GoogleApiClient 对象。另请参阅访问 Google APIWearable 类的参考页面

下表包含已弃用的组件及其替换组件:

已弃用组件 替换组件
CapabilityApi CapabilityClient
Channel ChannelClient.Channel
ChannelApi ChannelClient
DataApi DataClient
MessageApi MessageClient
NodeApi NodeClient

此外,还请注意以下事项:

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 类和关联接口(ConnectionCallbacksOnConnectionFailedListener等)的任何用例,并将其他监听器实现替换为新版本。要替换的实际方法通常与之前同名,因此主要更改类似于以下示例。

例如,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);
      }
    }