您可以使用 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 服務程式庫後,請使用活動 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();
您可以將多個 API 和多個範圍新增至同一個 GoogleApiClient
,方法是將其他呼叫附加至 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()
方法後才叫用這些方法。
以下是實作回呼介面並將其新增至 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
例項會在活動呼叫 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()
會在結果傳送前封鎖執行緒,因此應用程式絕不應在 UI 執行緒上對 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 // ... } }
存取 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 } // ... }
搭配其他 Google API 使用 Wearable API
如果應用程式除了使用其他 Google API 外,也使用 Wearable API,請呼叫 addApiIfAvailable()
方法,並傳入 Wearable API,以檢查該 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
) 而無法連線,系統會從 GoogleApiClient
中移除該 API,並讓用戶端繼續連線至其他 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
連線,您必須為回呼介面 ConnectionCallbacks
和 OnConnectionFailedListener
指定實作方式。當與 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 服務。