将 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 服务。

注意:此 API 目前仅适用于 Android 手机以及与 Android 手机配对的 Wear OS 手表。对于与 iOS 手机配对的 Wear OS 手表,在有互联网连接的情况下,应用可以查询其他云端 API。

已废弃组件的替换组件

当您使用扩展 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);
  }
}