Acessar APIs do Google com GoogleApiClient (descontinuado)

Você pode usar o objeto GoogleApiClient ("Cliente de API do Google") para acessar as APIs do Google fornecidas na biblioteca do Google Play Services, como Login do Google, Jogos e Drive. O cliente de APIs do Google fornece um ponto de entrada comum para o Google Play Services e gerencia a conexão de rede entre o dispositivo do usuário e cada serviço do Google.

No entanto, a interface GoogleApi mais recente e as implementações dela são mais fáceis de usar e são a maneira recomendada de acessar as APIs do Google Play Services. Consulte Como acessar as APIs do Google.

Este guia mostra como você pode:

  • Gerenciar automaticamente sua conexão com o Google Play Services.
  • Faça chamadas de API síncronas e assíncronas para qualquer um dos serviços do Google Play.
  • Nos raros casos em que isso for necessário, gerencie manualmente sua conexão com o Google Play Services. Para saber mais, consulte Conexões gerenciadas manualmente.
Figura 1: ilustração que mostra como o cliente da API do Google oferece uma interface para se conectar e fazer chamadas para qualquer um dos serviços do Google Play disponíveis, como o Google Play Games e o Google Drive.

Para começar, instale a biblioteca do Google Play Services (revisão 15 ou mais recente) para o SDK do Android. Siga as instruções em Configurar o SDK do Google Play Services, caso ainda não tenha feito isso.

Iniciar uma conexão gerenciada automaticamente

Depois que o projeto for vinculado à biblioteca do Google Play Services, crie uma instância de GoogleApiClient usando as APIs GoogleApiClient.Builder no método onCreate() da atividade. A classe GoogleApiClient.Builder fornece métodos que permitem especificar as APIs do Google que você quer usar e os escopos OAuth 2.0 desejados. Veja um exemplo de código que cria uma instância do GoogleApiClient que se conecta ao serviço do Google Drive:

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

É possível adicionar várias APIs e vários escopos ao mesmo GoogleApiClient anexando outras chamadas para addApi() e addScope().

Importante:se você estiver adicionando a API Wearable com outras APIs a uma GoogleApiClient, poderá encontrar erros de conexão do cliente em dispositivos que não têm o app para Wear OS instalado. Para evitar erros de conexão, chame o método addApiIfAvailable() e transmita a API Wearable para permitir que seu cliente processe corretamente a API ausente. Para mais informações, consulte Acessar a API Wearable.

Para iniciar uma conexão gerenciada automaticamente, especifique uma implementação para a interface OnConnectionFailedListener para receber erros de conexão sem solução. Quando a instância GoogleApiClient gerenciada automaticamente tentar se conectar às APIs do Google, ela vai mostrar automaticamente a interface para tentar corrigir falhas de conexão resolvíveis, por exemplo, se o Google Play Services precisar ser atualizado. Se ocorrer um erro que não pode ser resolvido, você vai receber uma chamada para onConnectionFailed().

Você também pode especificar uma implementação opcional para a interface ConnectionCallbacks se o app precisar saber quando a conexão gerenciada automaticamente foi estabelecida ou suspensa. Por exemplo, se o app faz chamadas para gravar dados nas APIs do Google, elas precisam ser invocadas somente após o método onConnected() ter sido chamado.

Confira abaixo um exemplo de atividade que implementa as interfaces de callback e as adiciona ao cliente da API do 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

        // ...
    }
}

Sua instância de GoogleApiClient se conectará automaticamente após a atividade chamar onStart() e será desconectada depois de chamar onStop(). Após a criação da GoogleApiClient, o app pode começar imediatamente a fazer solicitações de leitura para as APIs do Google sem aguardar a conclusão da conexão.

Comunicar-se com os Serviços do Google

Após a conexão, o cliente pode fazer chamadas de leitura e gravação usando as APIs específicas do serviço para as quais seu app está autorizado, conforme especificado pelas APIs e pelos escopos adicionados à instância do GoogleApiClient.

Observação:antes de fazer chamadas para serviços específicos do Google, talvez seja necessário registrar seu app no Google Play Console. Para instruções, consulte o guia de iniciação apropriado para a API que você está usando, como o Google Drive ou o Login do Google.

Quando você executa uma solicitação de leitura ou gravação usando GoogleApiClient, o cliente da API retorna um objeto PendingResult que representa a solicitação. Isso ocorre imediatamente, antes de a solicitação ser entregue ao serviço do Google que seu app está chamando.

Por exemplo, esta é uma solicitação para ler um arquivo do Google Drive que fornece um objeto PendingResult:

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

Depois que o app tiver um objeto PendingResult, ele poderá especificar se a solicitação será tratada como assíncrona ou síncrona.

Dica:seu app pode enfileirar solicitações de leitura sem estar conectado ao Google Play Services. Por exemplo, seu app pode chamar métodos para ler um arquivo do Google Drive, mesmo que a instância do GoogleApiClient já esteja conectada ou não. Depois que uma conexão é estabelecida, as solicitações de leitura na fila são executadas. As solicitações de gravação vão gerar um erro se o app chamar os métodos de gravação do Google Play Services enquanto o cliente da API do Google não estiver conectado.

Como usar chamadas assíncronas

Para tornar a solicitação assíncrona, chame setResultCallback() no PendingResult e forneça uma implementação da interface ResultCallback. Por exemplo, esta é a solicitação executada de forma assí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.
            // ...
        }
    });
}

Quando seu app recebe um objeto Result no callback onResult(), ele é entregue como uma instância da subclasse adequada, conforme especificado pela API que você está usando, como DriveApi.MetadataBufferResult.

Como usar chamadas síncronas

Se você quiser que seu código seja executado em uma ordem estritamente definida, talvez porque o resultado de uma chamada seja necessário como argumento para outra, é possível tornar sua solicitação síncrona chamando await() na PendingResult. Isso bloqueia a linha de execução e retorna o objeto Result quando a solicitação é concluída. Esse objeto é entregue como uma instância da subclasse apropriada, conforme especificado pela API que você está usando, por exemplo, DriveApi.MetadataBufferResult.

Como chamar await() bloqueia a linha de execução até o resultado chegar, seu app nunca deve fazer solicitações síncronas às APIs do Google na linha de execução de IU. Seu app pode criar uma nova linha de execução usando um objeto AsyncTask e usá-la para fazer a solicitação síncrona.

O exemplo a seguir mostra como fazer uma solicitação de arquivo para o Google Drive como uma chamada síncrona:

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

Acessar a API Wearable

A API Wearable oferece um canal de comunicação para apps executados em dispositivos portáteis e wearable. A API consiste em um conjunto de objetos de dados que o sistema pode enviar e sincronizar e listeners que notificam seus apps sobre eventos importantes usando uma camada de dados. A API Wearable está disponível em dispositivos com o Android 4.3 (nível 18 da API) ou versões mais recentes quando um dispositivo wearable está conectado e o app complementar do Wear OS está instalado.

Como usar a API Wearable independente

Caso seu app use a API Wearable, mas não outras APIs do Google, você pode adicionar essa API chamando o método addApi(). O exemplo abaixo mostra como adicionar a API Wearable à instância GoogleApiClient:

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

Em situações em que a API Wearable não está disponível, as solicitações de conexão que incluem a API Wearable falham com o código de erro API_UNAVAILABLE.

O exemplo abaixo mostra como determinar se a API Wearable está disponível:

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

Como usar a API Wearable com outras APIs do Google

Se o app usa a API Wearable, além de outras APIs do Google, chame o método addApiIfAvailable() e transmita a API Wearable para conferir se ele está disponível. Use essa verificação para ajudar o app a lidar com casos em que a API não está disponível.

O exemplo abaixo mostra como acessar a API Wearable com a 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();

No exemplo acima, o GoogleApiClient pode se conectar ao Google Drive sem se conectar à API Wearable, se ela não estiver disponível. Depois de conectar a instância GoogleApiClient, confira se a API Wearable está disponível antes de fazer chamadas de API:

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

Ignorar falhas de conexão da API

Se você chamar addApi() e o GoogleApiClient não conseguir se conectar a essa API, toda a operação de conexão desse cliente falhará e vai acionar o callback onConnectionFailed().

Você pode registrar uma falha de conexão de API para ser ignorada usando addApiIfAvailable(). Se uma API adicionada com addApiIfAvailable() não for conectada devido a um erro não recuperável (como API_UNAVAILABLE para Wear), ela será removida do GoogleApiClient e o cliente continuará a se conectar a outras APIs. No entanto, se alguma conexão de API falhar com um erro recuperável (como uma intent de resolução de consentimento do OAuth), a operação de conexão do cliente falhará. Ao usar uma conexão gerenciada automaticamente, o GoogleApiClient vai tentar resolver esses erros quando possível. Ao usar uma conexão gerenciada manualmente, um ConnectionResult contendo uma intent de resolução é entregue ao callback onConnectionFailed(). As falhas de conexão de API serão ignoradas somente se não houver resolução para a falha e a API tiver sido adicionada com addApiIfAvailable(). Para saber como implementar o tratamento manual de falhas de conexão, consulte Gerenciar falhas de conexão.

Como as APIs adicionadas com addApiIfAvailable() nem sempre estão presentes na instância GoogleApiClient conectada, proteja as chamadas a essas APIs adicionando uma verificação usando hasConnectedApi(). Para descobrir por que uma API específica não conseguiu se conectar quando toda a operação de conexão foi bem-sucedida para o cliente, chame getConnectionResult() e receba o código de erro do objeto ConnectionResult. Se o cliente chamar uma API quando não estiver conectado a ele, a chamada falhará com o código de status API_NOT_AVAILABLE.

Se a API que você está adicionando usando addApiIfAvailable() precisar de um ou mais escopos, adicione-os como parâmetros na chamada do método addApiIfAvailable() em vez de usar o método addScope(). Os escopos adicionados usando essa abordagem poderão não ser solicitados se a conexão com a API falhar antes de receber o consentimento do OAuth. No entanto, os escopos adicionados com addScope() são sempre solicitados.

Conexões gerenciadas manualmente

A maior parte deste guia mostra como usar o método enableAutoManage para iniciar uma conexão gerenciada automaticamente com erros resolvidos automaticamente. Em quase todos os casos, essa é a maneira melhor e mais fácil de se conectar às APIs do Google no seu app Android. No entanto, há algumas situações em que você gostaria de usar uma conexão gerenciada manualmente com as APIs do Google no seu app:

  • Para acessar as APIs do Google fora de uma atividade ou manter o controle da conexão da API
  • Para personalizar o tratamento de erros de conexão e a resolução

Esta seção fornece exemplos desses e de outros casos de uso avançados.

Iniciar uma conexão gerenciada manualmente

Para iniciar uma conexão gerenciada manualmente com GoogleApiClient, você precisa especificar uma implementação para as interfaces de callback, ConnectionCallbacks e OnConnectionFailedListener. Essas interfaces recebem callbacks em resposta ao método assíncrono connect() quando a conexão com o Google Play Services é bem-sucedida, falha ou é suspensa.

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

Ao gerenciar uma conexão manualmente, você precisará chamar os métodos connect() e disconnect() nos pontos certos do ciclo de vida do seu app. Em um contexto de atividade, a prática recomendada é chamar connect() no método onStart() da atividade e disconnect() no método onStop() da atividade. Os métodos connect() e disconnect() são chamados automaticamente ao usar uma conexão gerenciada automaticamente.

Se você estiver usando GoogleApiClient para se conectar a APIs que exigem autenticação, como o Google Drive ou o Google Play Games, há uma boa chance de que sua primeira tentativa de conexão falhe e seu app receba uma chamada para onConnectionFailed() com o erro SIGN_IN_REQUIRED, porque a conta de usuário não foi especificada.

Gerenciar falhas de conexão

Quando o app receber uma chamada para o callback onConnectionFailed(), chame hasResolution() no objeto ConnectionResult fornecido. Se ele retornar verdadeiro, seu app poderá solicitar que o usuário realize uma ação imediata para resolver o erro chamando startResolutionForResult() no objeto ConnectionResult. O método startResolutionForResult() se comporta da mesma forma que startActivityForResult() nessa situação e inicia uma atividade adequada ao contexto que ajuda o usuário a resolver o erro, como uma atividade que ajuda o usuário a selecionar uma conta.

Se hasResolution() retornar como falso, seu app precisará chamar GoogleApiAvailability.getErrorDialog(), transmitindo o código do erro para esse método. Isso retorna um Dialog fornecido pelo Google Play Services apropriado para o erro. A caixa de diálogo pode simplesmente fornecer uma mensagem explicando o erro ou também fornecer uma ação para iniciar uma atividade que possa resolver o erro (como quando o usuário precisa instalar uma versão mais recente do Google Play Services).

Por exemplo, o método de callback onConnectionFailed() vai ficar assim:

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

Depois que o usuário concluir a caixa de diálogo fornecida por startResolutionForResult() ou dispensar a mensagem fornecida por GoogleApiAvailability.getErrorDialog(), sua atividade receberá o callback onActivityResult() com o código de resultado RESULT_OK. Em seguida, o app poderá chamar connect() novamente. Exemplo:

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

No código acima, você provavelmente percebeu o booleano mResolvingError. Isso monitora o estado do app enquanto o usuário resolve o erro para evitar tentativas repetitivas de resolver o mesmo erro. Por exemplo, enquanto a caixa de diálogo do seletor de conta é mostrada para ajudar o usuário a resolver o erro SIGN_IN_REQUIRED, o usuário pode girar a tela. Isso recria a atividade e faz com que o método onStart() seja chamado de novo, o que chama connect() mais uma vez. Isso resulta em outra chamada para startResolutionForResult(), que cria outra caixa de diálogo do seletor de contas à frente da existente.

Esse booleano só atenderá à finalidade pretendida se persistir entre as instâncias da atividade. A próxima seção explica como manter o estado de tratamento de erros do app, apesar de outras ações do usuário ou eventos que ocorrem no dispositivo.

Manter o estado ao resolver um erro

Para evitar a execução do código em onConnectionFailed() enquanto uma tentativa anterior de resolver um erro estiver em andamento, você precisa manter um booleano que rastreie se o app já está tentando resolver um erro.

Conforme mostrado no exemplo de código acima, seu app precisa definir um booleano como true sempre que chamar startResolutionForResult() ou exibir a caixa de diálogo de GoogleApiAvailability.getErrorDialog(). Então, quando o app receber RESULT_OK no callback onActivityResult(), defina o booleano como false.

Para acompanhar o booleano entre as reinicializações de atividades (como quando o usuário gira a tela), salve o booleano nos dados de instância salvos da atividade usando 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);
}

Em seguida, recupere o estado salvo durante onCreate():

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

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

Agora está tudo pronto para executar seu app com segurança e se conectar manualmente ao Google Play Services.