透過 GoogleApiClient 存取 Google API (已淘汰)

您可以使用 GoogleApiClient (「Google API 用戶端」) 物件,存取 Google Play 服務程式庫 (例如 Google 登入、遊戲和雲端硬碟) 中提供的 Google API。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();

您可以在同一個 GoogleApiClient 中新增多個 API 和多個範圍,方法是對 addApi()addScope() 附加額外的呼叫。

重要事項:如果您將 Wearable API 與其他 API 一併新增至 GoogleApiClient,則可能會在未安裝 Wear OS 應用程式的裝置上發生用戶端連線錯誤。為避免發生連線錯誤,請呼叫 addApiIfAvailable() 方法並傳入 Wearable API,讓您的用戶端妥善處理缺少的 API。詳情請參閱「存取 Wearable API」。

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

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

以下範例活動實作回呼介面,並將這些介面新增至 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 執行個體會在呼叫 onStart() 後自動連線,並在呼叫 onStop() 後中斷連線。應用程式建立 GoogleApiClient 後,可以立即開始向 Google API 發出讀取要求,不必等待連線完成。

與 Google 服務通訊

連線後,用戶端就能使用您新增至 GoogleApiClient 執行個體的 API 和範圍,針對應用程式獲得授權的服務專屬 API 發出讀取和寫入呼叫。

注意:您必須先在 Google Developers Console 中註冊應用程式,才能呼叫特定 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() 會封鎖執行緒,直到結果送達為止,因此應用程式不應在 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 包含一組系統可以傳送及同步處理的資料物件,以及監聽器,使用資料層通知應用程式重要事件。如果裝置搭載 Android 4.3 (API 級別 18) 以上版本,且裝置上安裝了 Wear OS 隨附應用程式,就適用於搭載 Android 4.3 (API 級別 18) 以上版本的裝置。

單獨使用 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
    }
    // ...
}

搭配其他 Google API 使用 Wearable 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無法使用,便可以成功連線至 Google 雲端硬碟,無需連線至 Wearable API。連結 GoogleApiClient 執行個體後,請先確認 Wearable API 可用,再發出 API 呼叫:

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

忽略 API 連線失敗次數

如果您呼叫 addApi(),且 GoogleApiClient 無法成功連線至該 API,則該用戶端的整個連線作業都會失敗,並觸發 onConnectionFailed() 回呼。

您可以使用 addApiIfAvailable() 註冊要忽略的 API 連線失敗事件。如果因發生無法復原的錯誤 (例如 Wear 的 API_UNAVAILABLE) 導致使用 addApiIfAvailable() 新增的 API 無法連線,該 API 就會從 GoogleApiClient 中捨棄,而用戶端會繼續連線至其他 API。但是,如果任何 API 連線因可復原的錯誤 (例如 OAuth 同意解決意圖) 而失敗,用戶端連線作業就會失敗。使用自動代管連線時,GoogleApiClient 會盡可能嘗試解決這類錯誤。使用手動管理的連線時,系統會將含有解析度意圖的 ConnectionResult 傳遞至 onConnectionFailed() 回呼。只有在無法解決失敗問題並使用 addApiIfAvailable() 新增 API 時,系統才會忽略 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 服務。