通过 GoogleApiClient 访问 Google API(已弃用)

您可以使用 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 服务的连接。如需了解详情,请参阅手动管理的连接
图 1:此图显示了 Google API 客户端如何提供用于连接和调用任何可用的 Google Play 服务(例如 Google Play 游戏和 Google 云端硬盘)的界面。

如需开始使用,您必须先为 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() 附加额外的调用,向同一 GoogleApiClient 添加多个 API 和多个范围。

重要提示:将 Wearable API 与其他 API 一起添加到 GoogleApiClient 时,可能会在未安装 Wear OS 应用的设备上遇到客户端连接错误。为避免连接错误,请调用 addApiIfAvailable() 方法并传入 Wearable API,以允许您的客户端妥善处理缺少的 API。如需了解详情,请参阅访问 Wearable API

如需启动自动托管连接,您必须为 OnConnectionFailedListener 接口指定实现,以接收无法解析的连接错误。当您的自动管理的 GoogleApiClient 实例尝试连接到 Google API 时,它会自动显示界面,以尝试修复任何可解决的连接故障(例如,如果需要更新 Google Play 服务)。如果出现无法解决的错误,您将收到对 onConnectionFailed() 的调用。

如果您的应用需要知道自动管理的连接何时建立或暂停,您还可以为 ConnectionCallbacks 接口指定可选实现。例如,如果您的应用通过调用 Google API 写入数据,则只应在调用 onConnected() 方法后调用这些调用。

以下是一个实现回调接口并将其添加到 Google API 客户端的示例 activity:

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 Developer Console 中注册您的应用。如需了解相关说明,请参阅您使用的 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 {
    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
        // ...
    }
}

访问 Wearable API

Wearable API 为在手持设备和穿戴式设备上运行的应用提供通信通道。该 API 由系统可以发送和同步的一组数据对象,以及使用数据层向应用通知重要事件的监听器。在搭载 Android 4.3(API 级别 18)或更高版本的设备上,如果连接了穿戴式设备并且设备上安装了 Wear OS 配套应用,则可以使用 Wearable API

单独使用 Wearable 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 搭配使用

如果您的应用使用 Wearable API 以及其他 Google API,请调用 addApiIfAvailable() 方法并传入 Wearable API 以检查其是否可用。您可以使用此检查来帮助您的应用妥善处理 API 不可用的情况。

以下示例展示了如何访问 Wearable APIDrive 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();

在上面的示例中,如果 GoogleApiClient 不可用,则无需连接到 Wearable API,即可成功连接到 Google 云端硬盘。连接 GoogleApiClient 实例后,请确保 Wearable API 可用,然后再进行 API 调用:

boolean wearAvailable = mGoogleApiClient.hasConnectedApi(Wearable.API);

忽略 API 连接故障

如果您调用 addApi()GoogleApiClient 无法成功连接到该 API,则针对该客户端的整个连接操作将失败并触发 onConnectionFailed() 回调。

您可以使用 addApiIfAvailable() 注册要忽略的 API 连接失败。如果使用 addApiIfAvailable() 添加的 API 因不可恢复的错误(如 API_UNAVAILABLE 适用于 Wear)而无法连接,该 API 会从您的 GoogleApiClient 丢弃,而客户端会继续连接到其他 API。不过,如果任何 API 连接因可恢复的错误(例如 OAuth 同意解析 intent)失败,客户端连接操作就会失败。使用自动管理的连接时,GoogleApiClient 将尽可能尝试解决此类错误。使用手动管理的连接时,包含解析 intent 的 ConnectionResult 会被传递给 onConnectionFailed() 回调。仅当没有针对故障的解决方法并且使用 addApiIfAvailable() 添加了 API 时,系统才会忽略该故障。如需了解如何实现手动连接故障处理,请参阅处理连接故障

由于使用 addApiIfAvailable() 添加的 API 可能并不总是存在于已连接的 GoogleApiClient 实例中,因此您应使用 hasConnectedApi() 添加一项检查来保护对这些 API 的调用。如需了解在客户端的整个连接操作成功时特定 API 连接失败的原因,请调用 getConnectionResult() 并从 ConnectionResult 对象获取错误代码。如果您的客户端在未连接到客户端的情况下调用 API,调用会失败并显示 API_NOT_AVAILABLE 状态代码。

如果您通过 addApiIfAvailable() 添加的 API 需要一个或多个范围,请在 addApiIfAvailable() 方法调用中将这些范围添加为参数,而不是使用 addScope() 方法添加。如果在获得 OAuth 同意之前 API 连接失败,则可能无法请求使用此方法添加的范围,而使用 addScope() 添加的范围始终是请求的。

手动管理的连接

本指南的大部分内容将介绍如何使用 enableAutoManage 方法启动具有自动解决错误的自动管理连接。在几乎所有情况下,这是从 Android 应用连接到 Google API 的最佳和最简单的方法。不过,在某些情况下,您需要在应用中使用手动管理与 Google API 的连接:

  • 在活动之外访问 Google API 或保留对 API 连接的控制权
  • 自定义连接错误的处理和解决方法

本部分提供了这些及其他高级用例的示例。

启动手动管理的连接

如需启动与 GoogleApiClient 的手动管理连接,您必须为回调接口(ConnectionCallbacksOnConnectionFailedListener)指定实现。当与 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(如可帮助用户选择帐号的 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 服务了。