Accesso alle API di Google con GoogleApiClient (deprecato)

Puoi utilizzare l'oggetto GoogleApiClient ("Client API di Google") per accedere alle API di Google fornite nella libreria dei servizi Google Play (ad esempio Accedi con Google, Giochi e Drive). Il client API di Google fornisce un punto di contatto comune per i servizi Google Play e gestisce la connessione di rete tra il dispositivo dell'utente e ciascun servizio Google.

Tuttavia, l'interfaccia GoogleApi più recente e le relative implementazioni sono più facili da usare e sono il modo preferito per accedere alle API Play Services. Consulta Accesso alle API di Google.

Questa guida illustra come:

  • Gestisci automaticamente la connessione a Google Play Services.
  • Esegui chiamate API sincrone e asincrone a qualsiasi servizio Google Play.
  • Gestire manualmente la connessione a Google Play Services nei rari casi in cui sia necessario. Per saperne di più, consulta Connessioni gestite manualmente.
Figura 1: un'illustrazione che mostra come il client API di Google fornisca un'interfaccia per connettersi ed effettuare chiamate a qualsiasi servizio Google Play disponibile, come Google Play Giochi e Google Drive.

Per iniziare, devi prima installare la libreria Google Play Services (revisione 15 o successiva) per il tuo SDK Android. Se non l'hai ancora fatto, segui le istruzioni riportate in Configurare l'SDK Google Play Services.

Avviare una connessione gestita automaticamente

Dopo aver collegato il progetto alla libreria di Google Play Services, crea un'istanza di GoogleApiClient utilizzando le API GoogleApiClient.Builder nel metodo onCreate() della tua attività. La classe GoogleApiClient.Builder fornisce metodi che ti consentono di specificare le API di Google che vuoi utilizzare e gli ambiti OAuth 2.0 che preferisci. Ecco un esempio di codice che crea un'istanza di GoogleApiClient che si connette al servizio Google Drive:

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* FragmentActivity */,
                      this /* OnConnectionFailedListener */)
    .addApi(Drive.API)
    .addScope(Drive.SCOPE_FILE)
    .build();

Puoi aggiungere più API e più ambiti alla stessa GoogleApiClient aggiungendo chiamate aggiuntive a addApi() e addScope().

Importante:se aggiungi l'API Wearable insieme ad altre API a un GoogleApiClient, potresti riscontrare errori di connessione del client sui dispositivi su cui non è installata l'app Wear OS. Per evitare errori di connessione, chiama il metodo addApiIfAvailable() e passa l'API Wearable per consentire al client di gestire in modo corretto l'API mancante. Per ulteriori informazioni, consulta Accedere all'API Wearable.

Per avviare una connessione gestita automaticamente, devi specificare un'implementazione per l'interfaccia OnConnectionFailedListener per ricevere errori di connessione irrisolvibili. Quando l'istanza GoogleApiClient gestita automaticamente tenta di connettersi alle API di Google, viene visualizzata automaticamente l'interfaccia utente per tentare di correggere eventuali errori di connessione risolvibili (ad esempio, se è necessario aggiornare Google Play Services). Se si verifica un errore che non può essere risolto, riceverai una chiamata al numero onConnectionFailed().

Puoi anche specificare un'implementazione facoltativa per l'interfaccia ConnectionCallbacks se la tua app deve sapere quando la connessione gestita automaticamente viene stabilita o sospesa. Ad esempio, se la tua app effettua chiamate per scrivere dati nelle API di Google, queste devono essere richiamate solo dopo aver chiamato il metodo onConnected().

Di seguito è riportato un'attività di esempio che implementa le interfacce di callback e le aggiunge al client API 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

        // ...
    }
}

L'istanza GoogleApiClient si connetterà automaticamente dopo che la tua attività chiama onStart() e si disconnetterà dopo aver chiamato onStop(). L'app può iniziare immediatamente a effettuare richieste di lettura alle API di Google dopo la compilazione di GoogleApiClient, senza attendere il completamento della connessione.

Comunicare con i servizi Google

Dopo la connessione, il client può effettuare chiamate di lettura e scrittura utilizzando le API specifiche del servizio per le quali la tua app è autorizzata, come specificato dalle API e dagli ambiti che hai aggiunto all'istanza GoogleApiClient.

Nota:prima di effettuare chiamate a servizi Google specifici, potrebbe essere necessario registrare la tua app in Google Developers Console. Per istruzioni, consulta la guida introduttiva appropriata per l'API che stai utilizzando, ad esempio Google Drive o Accedi con Google.

Quando esegui una richiesta di lettura o scrittura utilizzando GoogleApiClient, il client API restituisce un oggetto PendingResult che rappresenta la richiesta. Ciò avviene immediatamente, prima che la richiesta venga inviata al servizio Google chiamato dall'app.

Ad esempio, di seguito è riportata una richiesta di lettura di un file da Google Drive che fornisce un oggetto PendingResult:

Query query = new Query.Builder()
        .addFilter(Filters.eq(SearchableField.TITLE, filename));
PendingResult<DriveApi.MetadataBufferResult> result = Drive.DriveApi.query(mGoogleApiClient, query);

Dopo che l'app ha un oggetto PendingResult, puoi specificare se la richiesta deve essere gestita come chiamata asincrona o sincrona.

Suggerimento:la tua app può mettere in coda le richieste di lettura quando non è connessa a Google Play Services. Ad esempio, l'app può chiamare metodi per leggere un file da Google Drive indipendentemente dal fatto che l'istanza di GoogleApiClient sia già connessa. Dopo aver stabilito una connessione, vengono eseguite le richieste di lettura in coda. Le richieste di scrittura generano un errore se la tua app chiama metodi di scrittura di Google Play Services mentre il client API di Google non è connesso.

Utilizzo di chiamate asincrone

Per rendere la richiesta asincrona, chiama setResultCallback() su PendingResult e fornisci un'implementazione dell'interfaccia ResultCallback. Ad esempio, di seguito è riportata la richiesta eseguita in modo asincrono:

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.
            // ...
        }
    });
}

Quando l'app riceve un oggetto Result nel callback onResult(), viene inviata come istanza della sottoclasse appropriata, come specificato dall'API in uso, ad esempio DriveApi.MetadataBufferResult.

Utilizzo di chiamate sincrone

Se vuoi che il codice venga eseguito in un ordine strettamente definito, ad esempio perché il risultato di una chiamata è necessario come argomento per un'altra, puoi rendere sincrona la richiesta chiamando await() su PendingResult. Questo blocca il thread e restituisce l'oggetto Result al termine della richiesta. Questo oggetto viene fornito come istanza della sottoclasse appropriata, come specificato dall'API in uso, ad esempio DriveApi.MetadataBufferResult.

Poiché la chiamata a await() blocca il thread fino all'arrivo del risultato, la tua app non deve mai effettuare richieste sincrone alle API di Google nel thread UI. L'app può creare un nuovo thread utilizzando un oggetto AsyncTask e utilizzarlo per effettuare la richiesta sincrona.

L'esempio seguente mostra come effettuare una richiesta di file a Google Drive come chiamata sincrona:

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
        // ...
    }
}

Accedere all'API Wearable

L'API Wearable fornisce un canale di comunicazione per le app che vengono eseguite su dispositivi portatili e indossabili. L'API è costituita da un insieme di oggetti dati che il sistema può inviare e sincronizzare e da ascoltatori che avvisano le tue app di eventi importanti utilizzando un livello dati. L'API Wearable è disponibile sui dispositivi con Android 4.3 (livello API 18) o versioni successive quando un dispositivo indossabile è connesso e l'app complementare Wear OS è installata sul dispositivo.

Utilizzare l'API Wearable autonoma

Se la tua app utilizza l'API Wearable, ma non altre API di Google, puoi aggiungerla chiamando il metodo addApi(). L'esempio seguente mostra come aggiungere l'API Wearable all'istanza GoogleApiClient:

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* FragmentActivity */,
                      this /* OnConnectionFailedListener */)
    .addApi(Wearable.API)
    .build();

Nelle situazioni in cui l'API Wearable non è disponibile, le richieste di connessione che includono l'API Wearable non vanno a buon fine e viene visualizzato il codice di errore API_UNAVAILABLE.

L'esempio seguente mostra come determinare se l'API Wearable è disponibile:

// 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
    }
    // ...
}

Utilizzare l'API Wearable con altre API di Google

Se la tua app utilizza l'API Wearable oltre ad altre API di Google, chiama il metodo addApiIfAvailable() e passa l'API Wearable per verificare se è disponibile. Puoi utilizzare questo controllo per aiutare la tua app a gestire in modo corretto i casi in cui l'API non è disponibile.

Il seguente esempio mostra come accedere all'API Wearable e all'API 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();

Nell'esempio riportato sopra, GoogleApiClient può connettersi correttamente a Google Drive senza connettersi all'API Wearable se non è disponibile. Dopo aver collegato l'istanza GoogleApiClient, assicurati che l'API Wearable sia disponibile prima di effettuare le chiamate API:

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

Ignorare gli errori di connessione dell'API

Se chiami addApi() e GoogleApiClient non riesce a collegarsi correttamente all'API, l'intera operazione di connessione per quel client non va a buon fine e viene attivato il callback onConnectionFailed().

Puoi registrare un errore di connessione all'API da ignorare utilizzando addApiIfAvailable(). Se un'API aggiunta con addApiIfAvailable() non riesce a connettersi a causa di un errore non recuperabile (ad esempio API_UNAVAILABLE per Wear), l'API viene rimossa da GoogleApiClient e il client procede a connettersi ad altre API. Tuttavia, se una connessione API non va a buon fine con un errore recuperabile (ad esempio un intento di risoluzione del consenso OAuth), l'operazione di connessione del client non va a buon fine. Quando si utilizza una connessione gestita automaticamente, GoogleApiClient tenterà di risolvere questi errori, se possibile. Quando utilizzi una connessione gestita manualmente, un ConnectionResult contenente un'intenzione di risoluzione viene inviato al callback onConnectionFailed(). I fallimenti di connessione dell'API vengono ignorati solo se non esiste una soluzione al problema e l'API è stata aggiunta con addApiIfAvailable(). Per scoprire come implementare la gestione manuale degli errori di connessione, consulta Gestire gli errori di connessione.

Poiché le API aggiunte con addApiIfAvailable() potrebbero non essere sempre presenti nell'istanza collegata GoogleApiClient, devi proteggere le chiamate a queste API aggiungendo un controllo utilizzando hasConnectedApi(). Per scoprire perché una determinata API non è riuscita a connettersi quando l'intera operazione di connessione è riuscita per il client, chiama getConnectionResult() e ottieni il codice di errore dall'oggetto ConnectionResult. Se il client chiama un'API quando non è collegato al client, la chiamata non va a buon fine e viene generato il codice di stato API_NOT_AVAILABLE.

Se l'API che stai aggiungendo tramite addApiIfAvailable() richiede uno o più ambiti, aggiungili come parametri nella chiamata al metodo addApiIfAvailable() anziché utilizzare il metodo addScope(). Gli ambiti aggiunti utilizzando questo approccio potrebbero non essere richiesti se la connessione all'API non va a buon fine prima di ottenere il consenso OAuth, mentre gli ambiti aggiunti con addScope() vengono sempre richiesti.

Connessioni gestite manualmente

La maggior parte di questa guida mostra come utilizzare il metodo enableAutoManage per avviare una connessione gestita automaticamente con errori risolti automaticamente. In quasi tutti i casi, questo è il modo migliore e più semplice per connettersi alle API di Google dalla tua app per Android. Tuttavia, in alcune situazioni potresti voler utilizzare una connessione gestita manualmente alle API di Google nella tua app:

  • Per accedere alle API di Google al di fuori di un'attività o mantenere il controllo della connessione API
  • Per personalizzare la gestione e la risoluzione degli errori di connessione

Questa sezione fornisce esempi di questi e altri casi d'uso avanzati.

Avviare una connessione gestita manualmente

Per avviare una connessione gestita manualmente a GoogleApiClient, devi specificare un'implementazione per le interfacce di callback ConnectionCallbacks e OnConnectionFailedListener. Queste interfacce ricevono i callback in risposta al metodo asincrono connect() quando la connessione a Google Play Services riesce, non va a buon fine o viene sospesa.

    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(Drive.API)
            .addScope(Drive.SCOPE_FILE)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build()

Quando gestisci una connessione manualmente, devi chiamare i metodi connect() e disconnect() nei punti giusti del ciclo di vita dell'app. In un contesto di attività, la best practice è chiamare connect() nel metodo onStart() della tua attività e disconnect() nel metodo onStop() della tua attività. I metodi connect() e disconnect() vengono chiamati automaticamente quando si utilizza una connessione gestita automaticamente.

Se utilizzi GoogleApiClient per connetterti ad API che richiedono l'autenticazione, come Google Drive o Google Play Giochi, è molto probabile che il primo tentativo di connessione non vada a buon fine e che la tua app riceva una chiamata a onConnectionFailed() con l'errore SIGN_IN_REQUIRED perché l'account utente non è stato specificato.

Gestire gli errori di connessione

Quando la tua app riceve una chiamata al callback onConnectionFailed(), devi chiamare hasResolution() sull'oggetto ConnectionResult fornito. Se restituisce true, la tua app può chiedere all'utente di intervenire immediatamente per risolvere l'errore chiamando startResolutionForResult() sull'oggetto ConnectionResult. In questa situazione, il metodo startResolutionForResult() si comporta come startActivityForResult() e avvia un'attività appropriata al contesto che aiuta l'utente a risolvere l'errore (ad esempio un'attività che aiuta l'utente a selezionare un account).

Se hasResolution() restituisce false, l'app deve chiamare GoogleApiAvailability.getErrorDialog(), passando il codice di errore a questo metodo. Verrà restituito un Dialog fornito dai servizi Google Play appropriato all'errore. La finestra di dialogo può semplicemente fornire un messaggio che spiega l'errore oppure può anche fornire un'azione per avviare un'attività che può risolvere l'errore (ad esempio quando l'utente deve installare una versione più recente di Google Play Services).

Ad esempio, il metodo di callback onConnectionFailed() dovrebbe ora avere il seguente aspetto:

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();
        }
    }
}

Dopo che l'utente ha completato la finestra di dialogo fornita da startResolutionForResult() o ha ignorato il messaggio fornito da GoogleApiAvailability.getErrorDialog(), la tua attività riceve il callback onActivityResult() con il codice di risultato RESULT_OK. L'app può quindi chiamare nuovamente connect(). Ad esempio:

@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();
            }
        }
    }
}

Nel codice riportato sopra, probabilmente hai notato il valore booleano mResolvingError. In questo modo viene monitorato lo stato dell'app mentre l'utente risolve l'errore per evitare tentativi ripetuti di risolvere lo stesso errore. Ad esempio, mentre viene visualizzata la finestra di dialogo per la scelta dell'account per aiutare l'utente a risolvere l'errore SIGN_IN_REQUIRED, l'utente può ruotare lo schermo. In questo modo viene ricreata la tua attività e il metodo onStart() viene chiamato nuovamente, che a sua volta richiama nuovamente connect(). Ciò comporta un'altra chiamata a startResolutionForResult(), che crea un'altra finestra di dialogo di selezione dell'account davanti a quella esistente.

Questo valore booleano ha lo scopo previsto solo se persiste nelle istanze di attività. La sezione successiva spiega come mantenere lo stato di gestione degli errori dell'app nonostante altre azioni dell'utente o altri eventi che si verificano sul dispositivo.

Mantieni lo stato durante la risoluzione di un errore

Per evitare di eseguire il codice in onConnectionFailed() mentre è in corso un precedente tentativo di risolvere un errore, devi conservare un valore booleano che monitori se la tua app sta già tentando di risolvere un errore.

Come mostrato nell'esempio di codice riportato sopra, l'app deve impostare un valore booleano su true ogni volta che chiama startResolutionForResult() o mostra la finestra di dialogo da GoogleApiAvailability.getErrorDialog(). Poi, quando l'app riceve RESULT_OK nel callback onActivityResult(), imposta il valore booleano su false.

Per tenere traccia del valore booleano durante i riavvii dell'attività (ad esempio quando l'utente ruota lo schermo), salvate il valore booleano nei dati di istanza salvati dell'attività utilizzando 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);
}

Quindi recupera lo stato salvato durante onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // ...
    mResolvingError = savedInstanceState != null
            && savedInstanceState.getBoolean(STATE_RESOLVING_ERROR, false);
}

Ora puoi eseguire in sicurezza la tua app e connetterti manualmente a Google Play Services.