Puedes usar el objeto GoogleApiClient
("Cliente de la API de Google") para acceder a las APIs de Google que se proporcionan en la biblioteca de Servicios de Google Play (como Acceso con Google, Juegos y Drive). El cliente de la API de Google proporciona un punto de entrada común a los Servicios de Google Play y administra la conexión de red entre el dispositivo del usuario y cada servicio de Google.
Sin embargo, la interfaz GoogleApi
más reciente y sus implementaciones son más fáciles de usar y son la forma preferida de acceder a las APIs de los Servicios de Play.
Consulta Cómo acceder a las APIs de Google.
En esta guía, se muestra cómo puedes hacer lo siguiente:
- Administra automáticamente tu conexión a los Servicios de Google Play.
- Realizar llamadas a la API síncronas y asíncronas a cualquiera de los Servicios de Google Play
- Administra manualmente tu conexión a los Servicios de Google Play en los casos excepcionales en los que sea necesario. Para obtener más información, consulta Conexiones administradas de forma manual.
Para comenzar, primero debes instalar la biblioteca de los Servicios de Google Play (revisión 15 o posterior) para tu SDK de Android. Si aún no lo hiciste, sigue las instrucciones que se indican en Cómo configurar el SDK de los Servicios de Google Play.
Cómo iniciar una conexión administrada automáticamente
Una vez que tu proyecto esté vinculado a la biblioteca de los Servicios de Google Play, crea una instancia de GoogleApiClient
con las APIs de GoogleApiClient.Builder
en el método onCreate()
de tu actividad. La clase GoogleApiClient.Builder
proporciona métodos que te permiten especificar las APIs de Google que deseas usar y los permisos de OAuth 2.0 que deseas. A continuación, se muestra un ejemplo de código que crea una instancia de GoogleApiClient
que se conecta con el servicio de Google Drive:
GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Drive.API) .addScope(Drive.SCOPE_FILE) .build();
Puedes agregar varias APIs y varios alcances al mismo GoogleApiClient
si agregas llamadas adicionales a addApi()
y addScope()
.
Importante: Si agregas la API de Wearable
junto con otras APIs a un GoogleApiClient
, es posible que experimentes errores de conexión de clientes en dispositivos que no tienen instalada la app para Wear OS. Para evitar errores de conexión, llama al método addApiIfAvailable()
y pasa la API de Wearable
para permitir que tu cliente controle de forma fluida la API faltante. Para obtener más información, consulta Cómo acceder a la API de Wearable.
Para iniciar una conexión administrada automáticamente, debes especificar una implementación para la interfaz OnConnectionFailedListener
para recibir errores de conexión que no se pueden resolver. Cuando tu instancia de GoogleApiClient
administrada automáticamente intente conectarse a las APIs de Google, mostrará automáticamente la IU para intentar corregir cualquier falla de conexión que se pueda resolver (por ejemplo, si se deben actualizar los Servicios de Google Play). Si se produce un error que no se puede resolver, recibirás una llamada a onConnectionFailed()
.
También puedes especificar una implementación opcional para la interfaz ConnectionCallbacks
si tu app necesita saber cuándo se establece o suspende la conexión administrada automáticamente. Por ejemplo, si tu app realiza llamadas para escribir datos en las APIs de Google, estas se deben invocar solo después de que se haya llamado al método onConnected()
.
Este es un ejemplo de actividad que implementa las interfaces de devolución de llamada y las agrega al cliente de la API de Google:
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 // ... } }
Tu instancia de GoogleApiClient
se conectará automáticamente después de que tu actividad llame a onStart()
y se desconecte después de llamar a onStop()
.
Tu app puede comenzar a realizar solicitudes de lectura a las APIs de Google de inmediato después de compilar GoogleApiClient
, sin esperar a que se complete la conexión.
Cómo comunicarse con los servicios de Google
Después de la conexión, tu cliente puede realizar llamadas de lectura y escritura con las APIs específicas del servicio para las que tu app está autorizada, como se especifica en las APIs y los permisos que agregaste a tu instancia de GoogleApiClient
.
Nota: Antes de realizar llamadas a servicios específicos de Google, es posible que debas registrar tu app en Google Play Console. Para obtener instrucciones, consulta la guía de introducción adecuada de la API que estés usando, como Google Drive o Acceso con Google.
Cuando realizas una solicitud de lectura o escritura con GoogleApiClient
, el cliente de la API muestra un objeto PendingResult
que representa la solicitud.
Esto ocurre de inmediato, antes de que la solicitud se envíe al servicio de Google al que llama tu app.
Por ejemplo, esta es una solicitud para leer un archivo de Google Drive que proporciona un objeto PendingResult
:
Query query = new Query.Builder() .addFilter(Filters.eq(SearchableField.TITLE, filename)); PendingResult<DriveApi.MetadataBufferResult> result = Drive.DriveApi.query(mGoogleApiClient, query);
Una vez que tu app tenga un objeto PendingResult
, puede especificar si la solicitud se controla como una llamada asíncrona o como una llamada síncrona.
Nota: Tu app puede poner en cola solicitudes de lectura mientras no está conectada a los Servicios de Google Play. Por ejemplo, tu app puede llamar a métodos para leer un archivo de Google Drive, independientemente de si tu instancia de GoogleApiClient
ya está conectada. Una vez que se establece una conexión, se ejecutan las solicitudes de lectura en cola. Las solicitudes de escritura generan un error si tu app llama a métodos de escritura de los Servicios de Google Play mientras tu cliente de la API de Google no está conectado.
Usa llamadas asíncronas
Para que la solicitud sea asíncrona, llama a setResultCallback()
en PendingResult
y proporciona una implementación de la interfaz ResultCallback
. Por ejemplo, esta es la solicitud que se ejecuta de forma asíncrona:
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. // ... } }); }
Cuando tu app recibe un objeto Result
en la devolución de llamada onResult()
, se entrega como una instancia de la subclase adecuada, como lo especifica la API que usas, como DriveApi.MetadataBufferResult
.
Cómo usar llamadas síncronas
Si deseas que tu código se ejecute en un orden estrictamente definido, quizás porque el resultado de una llamada se necesita como argumento para otra, puedes hacer que tu solicitud sea síncrona llamando a await()
en PendingResult
. Esto bloquea el subproceso y muestra el objeto Result
cuando se completa la solicitud. Este objeto se entrega como una instancia de la subclase adecuada, como lo especifica la API que usas, por ejemplo, DriveApi.MetadataBufferResult
.
Debido a que llamar a await()
bloquea el subproceso hasta que llega el resultado, tu app nunca debe realizar solicitudes síncronas a las APIs de Google en el subproceso de IU. Tu app puede crear un subproceso nuevo con un objeto AsyncTask
y usarlo para realizar la solicitud síncrona.
En el siguiente ejemplo, se muestra cómo realizar una solicitud de archivo a Google Drive como una llamada síncrona:
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 // ... } }
Accede a la API de Wearable
La API de Wearable proporciona un canal de comunicación para las apps que se ejecutan en dispositivos portátiles y wearables. La API consta de un conjunto de objetos de datos que el sistema puede enviar y sincronizar, y objetos de escucha que informan a tus apps sobre eventos importantes mediante una capa de datos. La API de Wearable está disponible en dispositivos con Android 4.3 (nivel de API 18) o versiones posteriores cuando se conecta un dispositivo wearable y se instala la app complementaria de Wear OS en el dispositivo.
Cómo usar la API de Wearable de forma independiente
Si tu app usa la API de Wearable, pero no otras APIs de Google, puedes agregar esta API llamando al método addApi()
. En el siguiente ejemplo, se muestra cómo agregar la API de Wearable a tu instancia de GoogleApiClient
:
GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Wearable.API) .build();
En situaciones en las que la API de Wearable no está disponible, las solicitudes de conexión que incluyen la API de Wearable fallan con el código de error API_UNAVAILABLE
.
En el siguiente ejemplo, se muestra cómo determinar si la API de Wearable está disponible:
// 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 } // ... }
Cómo usar la API de Wearable con otras APIs de Google
Si tu app usa la API de Wearable además de otras APIs de Google, llama al método addApiIfAvailable()
y pasa la API de Wearable para verificar si está disponible. Puedes usar esta verificación para ayudar a tu app a controlar de forma fluida los casos en los que la API no está disponible.
En el siguiente ejemplo, se muestra cómo acceder a la API de Wearable junto con la API de Drive:
// 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();
En el ejemplo anterior, GoogleApiClient
puede conectarse correctamente con Google Drive sin conectarse a la API de Wearable si no está disponible. Después de
conectar tu instancia de GoogleApiClient
, asegúrate de que la API de Wearable esté disponible antes de realizar las llamadas a la API:
boolean wearAvailable = mGoogleApiClient.hasConnectedApi(Wearable.API);
Cómo ignorar los errores de conexión de la API
Si llamas a addApi()
y GoogleApiClient
no puede conectarse correctamente a esa API, falla toda la operación de conexión de ese cliente y se activa la devolución de llamada de onConnectionFailed()
.
Puedes registrar una falla de conexión de API para que se ignore con addApiIfAvailable()
. Si una API agregada con addApiIfAvailable()
no se conecta debido a un error irrecuperable (como API_UNAVAILABLE
para Wear), esa API se descarta de tu GoogleApiClient
y el cliente se conecta a otras APIs. Sin embargo, si alguna conexión de API falla con un error recuperable (como un intent de resolución de consentimiento de OAuth), la operación de conexión del cliente falla. Cuando se usa una conexión administrada automáticamente, GoogleApiClient
intentará resolver esos errores cuando sea posible. Cuando se usa una conexión administrada de forma manual, se entrega un ConnectionResult
que contiene un intent de resolución a la devolución de llamada onConnectionFailed()
. Las fallas de conexión de la API se ignoran solo si no hay una resolución para la falla y la API se agregó con addApiIfAvailable()
.
Para aprender a implementar el control manual de fallas de conexión, consulta Controla las fallas de conexión.
Debido a que las APIs agregadas con addApiIfAvailable()
no siempre están presentes en la instancia conectada de GoogleApiClient
, debes proteger las llamadas a estas APIs agregando una verificación con hasConnectedApi()
. Para averiguar por qué una API en particular no pudo conectarse cuando toda la operación de conexión se realizó correctamente para el cliente, llama a getConnectionResult()
y obtén el código de error del objeto ConnectionResult
. Si tu cliente llama a una API cuando no está conectado a ella, la llamada fallará con el código de estado API_NOT_AVAILABLE
.
Si la API que agregas a través de addApiIfAvailable()
requiere uno o más permisos, agrégalos como parámetros en la llamada al método addApiIfAvailable()
en lugar de usar el método addScope()
. Es posible que no se soliciten los permisos agregados con este enfoque si la conexión a la API falla antes de obtener el consentimiento de OAuth, mientras que los permisos agregados con addScope()
siempre se solicitan.
Conexiones administradas de forma manual
En la mayor parte de esta guía, se muestra cómo usar el método enableAutoManage
para iniciar una conexión administrada automáticamente con errores resueltos automáticamente. En casi todos los casos, esta es la mejor y más fácil manera de conectarse a las APIs de Google desde tu app para Android. Sin embargo, hay algunas situaciones en las que te recomendamos usar una conexión administrada de forma manual a las APIs de Google en tu app:
- Para acceder a las APIs de Google fuera de una actividad o retener el control de la conexión de la API
- Cómo personalizar la administración y resolución de errores de conexión
En esta sección, se proporcionan ejemplos de estos y otros casos de uso avanzados.
Cómo iniciar una conexión administrada de forma manual
Para iniciar una conexión administrada de forma manual a GoogleApiClient
, debes especificar una implementación para las interfaces de devolución de llamada, ConnectionCallbacks
y OnConnectionFailedListener
.
Estas interfaces reciben devoluciones de llamada en respuesta al método asíncrono connect()
cuando la conexión a los Servicios de Google Play se realiza correctamente, falla o se suspende.
mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Drive.API) .addScope(Drive.SCOPE_FILE) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build()
Cuando administres una conexión de forma manual, deberás llamar a los métodos connect()
y disconnect()
en los momentos adecuados del ciclo de vida de tu app. En un contexto de actividad, la práctica recomendada es llamar a connect()
en el método onStart()
de tu actividad y a disconnect()
en el método onStop()
de tu actividad.
Los métodos connect()
y disconnect()
se llaman automáticamente cuando se usa una conexión administrada automáticamente.
Si usas GoogleApiClient
para conectarte a APIs que requieren autenticación, como Google Drive o Google Play Juegos, es probable que falle tu primer intento de conexión y tu app reciba una llamada a onConnectionFailed()
con el error SIGN_IN_REQUIRED
porque no se especificó la cuenta de usuario.
Controla las fallas de conexión
Cuando tu app recibe una llamada a la devolución de llamada onConnectionFailed()
, debes llamar a hasResolution()
en el objeto ConnectionResult
proporcionado. Si muestra verdadero, tu app puede solicitarle al usuario que tome medidas inmediatas para resolver el error llamando a startResolutionForResult()
en el objeto ConnectionResult
.
El método startResolutionForResult()
se comporta de la misma manera que startActivityForResult()
en esta situación y, además, inicia una actividad adecuada al contexto que ayuda al usuario a resolver el error (como una actividad que ayuda al usuario a seleccionar una cuenta).
Si hasResolution()
muestra el valor "false", tu app debe llamar a GoogleApiAvailability.getErrorDialog()
y pasarle el código de error a este método. Se muestra un Dialog
proporcionado por los servicios de Google Play que es adecuado para el error. El diálogo puede proporcionar un mensaje que explique el error o una acción para iniciar una actividad que pueda resolverlo (por ejemplo, cuando el usuario necesita instalar una versión más reciente de los Servicios de Google Play).
Por ejemplo, tu método de devolución de llamada onConnectionFailed()
ahora debería verse de la siguiente manera:
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(); } } }
Después de que el usuario complete el diálogo que proporciona startResolutionForResult()
o descarte el mensaje que proporciona GoogleApiAvailability.getErrorDialog()
, tu actividad recibirá la devolución de llamada onActivityResult()
con el código de resultado RESULT_OK
.
Luego, tu app puede volver a llamar a connect()
.
Por ejemplo:
@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(); } } } }
En el código anterior, es probable que hayas notado el valor booleano, mResolvingError
. Esto realiza un seguimiento del estado de la app mientras el usuario resuelve el error para evitar intentos repetitivos de resolver el mismo error. Por ejemplo, mientras se muestra el diálogo del selector de cuentas para ayudar al usuario a resolver el error SIGN_IN_REQUIRED
, el usuario puede rotar la pantalla. Esto recrea tu actividad y hace que se vuelva a llamar al método onStart()
, que luego vuelve a llamar a connect()
. Esto genera otra llamada a startResolutionForResult()
, que crea otro diálogo del selector de cuentas frente al existente.
Este valor booleano cumple con su propósito solo si persiste en todas las instancias de actividad. En la siguiente sección, se explica cómo mantener el estado de control de errores de tu app a pesar de otras acciones del usuario o eventos que ocurren en el dispositivo.
Mantén el estado mientras resuelves un error
Para evitar ejecutar el código en onConnectionFailed()
mientras se está realizando un intento anterior de resolver un error, debes retener un valor booleano que haga un seguimiento de si tu app ya está intentando resolver un error.
Como se muestra en el ejemplo de código anterior, tu app debe establecer un valor booleano en true
cada vez que llame a startResolutionForResult()
o muestre el diálogo de GoogleApiAvailability.getErrorDialog()
.
Luego, cuando tu app reciba RESULT_OK
en la devolución de llamada onActivityResult()
, establece el valor booleano en false
.
Para hacer un seguimiento del valor booleano en los reinicios de la actividad (como cuando el usuario rota la pantalla), guarda el valor booleano en los datos de la instancia guardada de la actividad con 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); }
Luego, recupera el estado guardado durante onCreate()
:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... mResolvingError = savedInstanceState != null && savedInstanceState.getBoolean(STATE_RESOLVING_ERROR, false); }
Ya puedes ejecutar tu app de forma segura y conectarte manualmente a los Servicios de Google Play.