您可以使用 GoogleApiClient
(“Google API 客户端”)对象访问 Google Play 服务库中提供的 Google API(例如 Google 登录、游戏和云端硬盘)。Google API 客户端提供 Google Play 服务的通用入口点,并管理用户设备与各项 Google 服务之间的网络连接。
不过,较新的 GoogleApi
接口及其实现更易于使用,是访问 Play 服务 API 的首选方式。请参阅访问 Google API。
本指南将介绍如何:
- 自动管理您与 Google Play 服务的关联。
- 对任何 Google Play 服务执行同步和异步 API 调用。
- 在极少数情况下,您可以手动管理与 Google Play 服务的关联。如需了解详情,请参阅手动管理的连接。
首先,您必须为 Android SDK 安装 Google Play 服务库(修订版 15 或更高版本)。如果您尚未完成,请按照设置 Google Play 服务 SDK 中的说明操作。
启动自动管理的连接
将项目关联到 Google Play 服务库后,请在 activity 的 onCreate()
方法中使用 GoogleApiClient.Builder
API 创建 GoogleApiClient
实例。GoogleApiClient.Builder
类提供了一些方法,可让您指定要使用的 Google API 和所需的 OAuth 2.0 范围。以下代码示例创建了一个与 Google 云端硬盘服务连接的 GoogleApiClient
实例:
GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Drive.API) .addScope(Drive.SCOPE_FILE) .build();
您可以通过将其他调用附加到 addApi()
和 addScope()
,将多个 API 和多个镜重添加到同一 GoogleApiClient
。
重要提示:如果您将 Wearable
API 与其他 API 一起添加到 GoogleApiClient
,则在未安装 Wear OS 应用的设备上可能会遇到客户端连接错误。为避免连接错误,请调用 addApiIfAvailable()
方法并传入 Wearable
API,以便客户端能够妥善处理缺失的 API。如需了解详情,请参阅访问 Wearables API。
如需开始自动管理连接,您必须为 OnConnectionFailedListener
接口指定实现,以接收无法解析的连接错误。当您的自动管理型 GoogleApiClient
实例尝试连接到 Google API 时,它会自动显示界面,以尝试修复任何可解决的连接失败问题(例如,如果 Google Play 服务需要更新)。如果发生无法解决的错误,您将收到对 onConnectionFailed()
的调用。
如果您的应用需要知道自动管理的连接何时建立或暂停,您还可以为 ConnectionCallbacks
接口指定可选实现。例如,如果您的应用调用 Google API 以将数据写入其中,则应仅在调用 onConnected()
方法后调用这些 API。
以下示例 activity 实现了回调接口并将其添加到 Google API 客户端:
import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; import gms.drive.*; import android.support.v4.app.FragmentActivity; public class MyActivity extends FragmentActivity implements OnConnectionFailedListener { private GoogleApiClient mGoogleApiClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a GoogleApiClient instance mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Drive.API) .addScope(Drive.SCOPE_FILE) .build(); // ... } @Override public void onConnectionFailed(ConnectionResult result) { // An unresolvable error has occurred and a connection to Google APIs // could not be established. Display an error message, or handle // the failure silently // ... } }
您的 GoogleApiClient
实例会在 activity 调用 onStart()
后自动连接,并在调用 onStop()
后断开连接。构建 GoogleApiClient
后,您的应用可以立即开始向 Google API 发出读取请求,而无需等待连接完成。
与 Google 服务通信
连接后,您的客户端可以使用您的应用已获授权的服务专用 API 进行读写调用,具体取决于您添加到 GoogleApiClient
实例中的 API 和镜重范围。
注意:在调用特定 Google 服务之前,您可能需要先在 Google 开发者控制台中注册您的应用。如需了解相关说明,请参阅您所使用的 API(例如 Google 云端硬盘或 Google 登录)对应的使用入门指南。
当您使用 GoogleApiClient
执行读取或写入请求时,API 客户端会返回一个代表请求的 PendingResult
对象。这会在请求传送到应用调用的 Google 服务之前立即发生。
例如,以下是从 Google 云端硬盘读取文件的请求,其中提供了 PendingResult
对象:
Query query = new Query.Builder() .addFilter(Filters.eq(SearchableField.TITLE, filename)); PendingResult<DriveApi.MetadataBufferResult> result = Drive.DriveApi.query(mGoogleApiClient, query);
在应用拥有 PendingResult
对象后,应用可以指定是将请求作为异步调用还是同步调用进行处理。
提示:您的应用可以在未连接到 Google Play 服务时将读取请求加入队列。例如,无论 GoogleApiClient
实例是否已连接,您的应用都可以调用方法来从 Google 云端硬盘读取文件。建立连接后,队列中的读取请求会执行。如果您的应用在 Google API 客户端未连接的情况下调用 Google Play 服务写入方法,写入请求会生成错误。
使用异步调用
如需将请求设为异步,请对 PendingResult
调用 setResultCallback()
,并提供 ResultCallback
接口的实现。例如,下面是异步执行的请求:
private void loadFile(String filename) { // Create a query for a specific filename in Drive. Query query = new Query.Builder() .addFilter(Filters.eq(SearchableField.TITLE, filename)) .build(); // Invoke the query asynchronously with a callback method Drive.DriveApi.query(mGoogleApiClient, query) .setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() { @Override public void onResult(DriveApi.MetadataBufferResult result) { // Success! Handle the query result. // ... } }); }
当您的应用在 onResult()
回调中收到 Result
对象时,该对象会作为您所用 API(例如 DriveApi.MetadataBufferResult
)指定的适当子类的实例进行传送。
使用同步调用
如果您希望代码按严格定义的顺序执行(可能是因为需要将一个调用的结果用作另一个调用的参数),则可以通过对 PendingResult
调用 await()
来使请求同步。这会阻塞线程,并在请求完成时返回 Result
对象。此对象将作为您所用 API 指定的适当子类的实例传送,例如 DriveApi.MetadataBufferResult
。
由于调用 await()
会阻塞线程,直到结果到达为止,因此您的应用绝不应在界面线程上向 Google API 发出同步请求。您的应用可以使用 AsyncTask
对象创建新线程,并使用该线程发出同步请求。
以下示例展示了如何以同步调用的形式向 Google 云端硬盘发出文件请求:
private void loadFile(String filename) { new GetFileTask().execute(filename); } private class GetFileTask extends AsyncTask<String, Void, Void> { protected void doInBackground(String filename) { Query query = new Query.Builder() .addFilter(Filters.eq(SearchableField.TITLE, filename)) .build(); // Invoke the query synchronously DriveApi.MetadataBufferResult result = Drive.DriveApi.query(mGoogleApiClient, query).await(); // Continue doing other stuff synchronously // ... } }
访问 Wearables API
Wearable API 为在手持式设备和穿戴式设备上运行的应用提供了通信通道。该 API 由系统可以发送和同步的一组数据对象以及用于使用数据层向应用通知重要事件的监听器组成。当穿戴式设备连接到搭载 Android 4.3(API 级别 18)或更高版本的设备,并且设备上安装了 Wear OS 配套应用时,Wearable API 可在该设备上使用。
使用独立的 Wear OS API
如果您的应用使用 Wearable API,但不使用其他 Google API,则可以通过调用 addApi()
方法来添加此 API。以下示例展示了如何将 Wearable API 添加到 GoogleApiClient
实例:
GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Wearable.API) .build();
如果 Wearable API 不可用,包含 Wearable API 的连接请求将会失败并返回 API_UNAVAILABLE
错误代码。
以下示例展示了如何确定 Wearable API 是否可用:
// Connection failed listener method for a client that only // requests access to the Wearable API @Override public void onConnectionFailed(ConnectionResult result) { if (result.getErrorCode() == ConnectionResult.API_UNAVAILABLE) { // The Wearable API is unavailable } // ... }
将 Wearable API 与其他 Google API 搭配使用
如果您的应用除了其他 Google API 之外还使用 Wearable API,请调用 addApiIfAvailable()
方法并传入 Wearable API 以检查其是否可用。您可以使用此检查来帮助应用妥善处理 API 不可用的情况。
以下示例展示了如何同时访问 Wearable API 和 Drive API:
// Create a GoogleApiClient instance mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Drive.API) .addApiIfAvailable(Wearable.API) .addScope(Drive.SCOPE_FILE) .build();
在上面的示例中,如果 Wearable API 不可用,GoogleApiClient
可以成功连接到 Google 云端硬盘,而无需连接到 Wearable API。连接 GoogleApiClient
实例后,请确保 Wearable API 可用,然后再发出 API 调用:
boolean wearAvailable = mGoogleApiClient.hasConnectedApi(Wearable.API);
忽略 API 连接失败
如果您调用 addApi()
,而 GoogleApiClient
无法成功连接到该 API,则该客户端的整个连接操作都会失败,并触发 onConnectionFailed()
回调。
您可以使用 addApiIfAvailable()
注册要忽略的 API 连接失败。如果使用 addApiIfAvailable()
添加的 API 因不可恢复的错误(例如 Wear 设备的 API_UNAVAILABLE
)而无法连接,该 API 会从 GoogleApiClient
中移除,客户端会继续连接到其他 API。但是,如果任何 API 连接因可恢复的错误(例如 OAuth 意见征求解决 intent)而失败,客户端连接操作也会失败。使用自动管理的连接时,GoogleApiClient
会尽可能尝试解决此类错误。使用手动管理的连接时,系统会将包含解决方案 intent 的 ConnectionResult
传递给 onConnectionFailed()
回调。仅当无法解决 API 连接失败问题且 API 是使用 addApiIfAvailable()
添加的,系统才会忽略 API 连接失败。如需了解如何实现手动连接失败处理,请参阅处理连接失败。
由于使用 addApiIfAvailable()
添加的 API 可能并不总是存在于关联的 GoogleApiClient
实例中,因此您应使用 hasConnectedApi()
添加检查,以保护对这些 API 的调用。如需了解在客户端的整个连接操作成功时特定 API 未能连接的原因,请调用 getConnectionResult()
并从 ConnectionResult
对象获取错误代码。如果您的客户端在未连接到服务器的情况下调用 API,则调用会失败并返回 API_NOT_AVAILABLE
状态代码。
如果您通过 addApiIfAvailable()
添加的 API 需要一个或多个作用域,请在 addApiIfAvailable()
方法调用中将这些作用域添加为参数,而不是使用 addScope()
方法。如果 API 连接在获得 OAuth 意见征求同意之前失败,系统可能不会请求使用此方法添加的范围,但系统始终会请求使用 addScope()
添加的范围。
手动管理的连接
本指南的大部分内容介绍了如何使用 enableAutoManage
方法启动自动管理的连接,并自动解决错误。在大多数情况下,这是从 Android 应用连接到 Google API 的最简单、最有效的方法。不过,在某些情况下,您可能需要在应用中使用手动管理的 Google API 连接:
- 如需在 activity 之外访问 Google API 或保留对 API 连接的控制
- 自定义连接错误处理和解决方法
本部分提供了这些和其他高级用例的示例。
启动手动管理的连接
如需发起与 GoogleApiClient
的手动管理连接,您必须为回调接口 ConnectionCallbacks
和 OnConnectionFailedListener
指定实现。当与 Google Play 服务的连接成功、失败或被中止时,这些接口会收到回调,以响应异步 connect()
方法。
mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Drive.API) .addScope(Drive.SCOPE_FILE) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build()
手动管理连接时,您需要在应用生命周期的正确时间点调用 connect()
和 disconnect()
方法。在 activity 上下文中,最佳实践是在 activity 的 onStart()
方法中调用 connect()
,并在 activity 的 onStop()
方法中调用 disconnect()
。使用自动管理的连接时,系统会自动调用 connect()
和 disconnect()
方法。
如果您使用 GoogleApiClient
连接到需要身份验证的 API(例如 Google 云端硬盘或 Google Play 游戏),您的首次连接尝试很可能会失败,并且您的应用会收到对 onConnectionFailed()
的调用,其中包含 SIGN_IN_REQUIRED
错误,因为未指定用户账号。
处理连接失败
当您的应用收到对 onConnectionFailed()
回调的调用时,您应对提供的 ConnectionResult
对象调用 hasResolution()
。如果它返回 true,您的应用可以请求用户通过对 ConnectionResult
对象调用 startResolutionForResult()
来立即采取措施来解决错误。在这种情况下,startResolutionForResult()
方法的行为与 startActivityForResult()
相同,并会启动一个适合上下文的 activity 来帮助用户解决错误(例如,帮助用户选择账号的 activity)。
如果 hasResolution()
返回 false,您的应用应调用 GoogleApiAvailability.getErrorDialog()
,并将错误代码传递给此方法。这会返回 Google Play 服务提供的与错误相符的 Dialog
。该对话框可能只提供一条说明错误的消息,也可能提供用于启动可解决错误的 activity 的操作(例如,当用户需要安装较新版本的 Google Play 服务时)。
例如,您的 onConnectionFailed()
回调方法现在应如下所示:
public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener { // Request code to use when launching the resolution activity private static final int REQUEST_RESOLVE_ERROR = 1001; // Unique tag for the error dialog fragment private static final String DIALOG_ERROR = "dialog_error"; // Bool to track whether the app is already resolving an error private boolean mResolvingError = false; // ... @Override public void onConnectionFailed(ConnectionResult result) { if (mResolvingError) { // Already attempting to resolve an error. return; } else if (result.hasResolution()) { try { mResolvingError = true; result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR); } catch (SendIntentException e) { // There was an error with the resolution intent. Try again. mGoogleApiClient.connect(); } } else { // Show dialog using GoogleApiAvailability.getErrorDialog() showErrorDialog(result.getErrorCode()); mResolvingError = true; } } // The rest of this code is all about building the error dialog /* Creates a dialog for an error message */ private void showErrorDialog(int errorCode) { // Create a fragment for the error dialog ErrorDialogFragment dialogFragment = new ErrorDialogFragment(); // Pass the error that should be displayed Bundle args = new Bundle(); args.putInt(DIALOG_ERROR, errorCode); dialogFragment.setArguments(args); dialogFragment.show(getSupportFragmentManager(), "errordialog"); } /* Called from ErrorDialogFragment when the dialog is dismissed. */ public void onDialogDismissed() { mResolvingError = false; } /* A fragment to display an error dialog */ public static class ErrorDialogFragment extends DialogFragment { public ErrorDialogFragment() { } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Get the error code and retrieve the appropriate dialog int errorCode = this.getArguments().getInt(DIALOG_ERROR); return GoogleApiAvailability.getInstance().getErrorDialog( this.getActivity(), errorCode, REQUEST_RESOLVE_ERROR); } @Override public void onDismiss(DialogInterface dialog) { ((MyActivity) getActivity()).onDialogDismissed(); } } }
用户完成 startResolutionForResult()
提供的对话框或关闭 GoogleApiAvailability.getErrorDialog()
提供的消息后,您的 activity 会收到包含 RESULT_OK
结果代码的 onActivityResult()
回调。然后,您的应用可以再次调用 connect()
。例如:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_RESOLVE_ERROR) { mResolvingError = false; if (resultCode == RESULT_OK) { // Make sure the app is not already connected or attempting to connect if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) { mGoogleApiClient.connect(); } } } }
在上面的代码中,您可能注意到了布尔值 mResolvingError
。这会在用户解决错误时跟踪应用状态,以避免重复尝试解决同一错误。例如,在系统显示账号选择器对话框以帮助用户解决 SIGN_IN_REQUIRED
错误时,用户可能会旋转屏幕。这会重新创建您的 activity,并导致系统再次调用 onStart()
方法,然后该方法会再次调用 connect()
。这会导致再次调用 startResolutionForResult()
,从而在现有账号选择器对话框前面创建另一个账号选择器对话框。
只有当此布尔值在 activity 实例之间保持不变时,才能发挥预期作用。下一部分将介绍如何在设备上发生其他用户操作或事件时维护应用的错误处理状态。
在解决错误时保持状态
为了避免在之前尝试解决错误时执行 onConnectionFailed()
中的代码,您需要保留一个布尔值,用于跟踪应用是否已尝试解决错误。
如上面的代码示例所示,您的应用应在每次调用 startResolutionForResult()
或通过 GoogleApiAvailability.getErrorDialog()
显示对话框时,将布尔值设置为 true
。然后,当您的应用在 onActivityResult()
回调中收到 RESULT_OK
时,将布尔值设置为 false
。
如需在 activity 重启(例如用户旋转屏幕时)跟踪该布尔值,请使用 onSaveInstanceState()
将该布尔值保存在 activity 的已保存实例数据中:
private static final String STATE_RESOLVING_ERROR = "resolving_error"; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(STATE_RESOLVING_ERROR, mResolvingError); }
然后,在 onCreate()
期间恢复已保存的状态:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... mResolvingError = savedInstanceState != null && savedInstanceState.getBoolean(STATE_RESOLVING_ERROR, false); }
现在,您可以安全地运行应用并手动连接到 Google Play 服务了。