透過 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 服務程式庫後,請使用活動 onCreate() 方法中的 GoogleApiClient.Builder API,建立 GoogleApiClient 執行個體。GoogleApiClient.Builder 類別提供的方法可讓您指定要使用的 Google API 和所需的 OAuth 2.0 範圍。以下程式碼範例會建立 GoogleApiClient 執行個體,並連線至 Google 雲端硬碟服務:

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。詳情請參閱「存取 Wearable API」。

如要開始自動代管的連線,您必須指定 OnConnectionFailedListener 介面的實作方式,才能收到無法解決的連線錯誤。自動管理的 GoogleApiClient 執行個體嘗試連線至 Google API 時,系統會自動顯示 UI,嘗試修正任何可解析的連線失敗問題 (例如 Google Play 服務需要更新時)。如果發生無法解決的錯誤,您會收到對 onConnectionFailed() 的呼叫。

如果應用程式需要知道自動管理連線何時建立或暫停,您也可以為 ConnectionCallbacks 介面指定選用實作。舉例來說,如果應用程式會呼叫將資料寫入 Google API,則只有在呼叫 onConnected() 方法後,才能叫用這些呼叫。

以下活動範例會實作回呼介面,並將介面新增至 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

        // ...
    }
}

在活動呼叫 onStart() 後,GoogleApiClient 執行個體會自動連線,並在呼叫 onStop() 後中斷連線。您的應用程式可以在建構 GoogleApiClient 後,立即開始向 Google API 發出讀取要求,無須等待連線完成。

與 Google 服務通訊

連線之後,用戶端可根據您新增至 GoogleApiClient 執行個體的 API 和範圍,使用已授權應用程式的服務專用 API 發出讀取和寫入呼叫。

注意:呼叫特定 Google 服務之前,您必須先在 Google Developer Console 註冊應用程式。如需相關操作說明,請參閱您所需 API 的適用入門指南,例如 Google 雲端硬碟Google 登入

使用 GoogleApiClient 執行讀取或寫入要求時,API 用戶端會傳回代表要求的 PendingResult 物件。該要求會立即發生,在要求傳送至應用程式呼叫的 Google 服務之前。

舉例來說,以下要求從提供 PendingResult 物件的 Google 雲端硬碟讀取檔案:

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() 會封鎖執行緒,直到結果送達為止,因此應用程式一律不應在 UI 執行緒上向 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 包含一組系統可傳送和同步處理的資料物件,以及使用資料層通知應用程式重大事件的監聽器。如果穿戴式裝置連線且安裝了 Wear OS 隨附應用程式,則裝置必須搭載 Android 4.3 (API 級別 18) 以上版本,才能使用 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 搭配使用

如果您的應用程式除了其他 Google API 也使用 Wearable 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();

在上述範例中,如果無法使用 Wearable APIGoogleApiClient 就可以順利連線至 Google 雲端硬碟,連結 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 同意聲明解析意圖),用戶端連線作業就會失敗。使用自動代管的連線時,GoogleApiClient 會盡可能嘗試解決這類錯誤。使用手動管理的連線時,含有解析意圖的 ConnectionResult 會傳送至 onConnectionFailed() 回呼。只有在沒有該故障的解決方式,且該 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 的連線:

  • 在活動外存取 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() 方法。在活動結構定義中,最佳做法是在活動的 onStart() 方法中呼叫 connect(),並在活動的 onStop() 方法中呼叫 disconnect()。使用自動代管連線時,系統會自動呼叫 connect()disconnect() 方法。

如果使用 GoogleApiClient 連線至需要驗證的 API (例如 Google 雲端硬碟或 Google Play 遊戲),可能會在未指定使用者帳戶的情況下收到對 onConnectionFailed() 的呼叫並傳回 SIGN_IN_REQUIRED 錯誤。

處理連線失敗問題

應用程式收到對 onConnectionFailed() 回呼的呼叫時,應在提供的 ConnectionResult 物件上呼叫 hasResolution()。如果傳回 true,應用程式可以在 ConnectionResult 物件上呼叫 startResolutionForResult(),要求使用者立即採取行動以解決錯誤。在這種情況下,startResolutionForResult() 方法的行為與 startActivityForResult() 相同,並根據結構定義啟動適合使用者的活動,協助使用者解決錯誤 (例如可協助使用者選取帳戶的活動)。

如果 hasResolution() 傳回 false,應用程式應呼叫 GoogleApiAvailability.getErrorDialog(),並將錯誤代碼傳遞至這個方法。此方法會傳回 Google Play 服務提供適用於該錯誤的 Dialog。對話方塊可能會提供說明錯誤的訊息,也可能提供啟動活動來解決錯誤 (例如,使用者需要安裝新版 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() 提供的訊息後,活動會收到含有 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 錯誤,但使用者可能會旋轉畫面。這樣做會重新建立活動,並再次呼叫 onStart() 方法,接著會再次呼叫 connect()。這會導致系統再次呼叫 startResolutionForResult(),這會在現有對話方塊的前面建立另一個帳戶挑選器對話方塊。

這個布林值只有在各個活動例項中持續時,才會維持其預期用途。下一節將說明如何即使裝置上發生其他使用者操作或事件,仍能維持應用程式的錯誤處理狀態。

修正錯誤時保留狀態

為避免在先前嘗試解決錯誤時,在 onConnectionFailed() 中執行程式碼,您必須保留布林值來追蹤應用程式是否已嘗試解決錯誤。

如以上程式碼範例所示,應用程式每次呼叫 startResolutionForResult() 或顯示 GoogleApiAvailability.getErrorDialog() 的對話方塊時,都應將布林值設為 true。接著,當應用程式在 onActivityResult() 回呼中收到 RESULT_OK 時,請將布林值設為 false

如要在活動重新啟動時追蹤布林值 (例如使用者旋轉螢幕時),請使用 onSaveInstanceState() 將布林值儲存至活動的儲存例項資料中:

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 服務。