Доступ к API Google с помощью GoogleApiClient (устарело)

Вы можете использовать объект GoogleApiClient («Клиент API Google») для доступа к API Google, предоставленным в библиотеке сервисов Google Play (например, вход в Google, Игры и Диск). Клиент Google API предоставляет общую точку входа в службы Google Play и управляет сетевым соединением между устройством пользователя и каждой службой Google.

Однако новый интерфейс GoogleApi и его реализации проще в использовании и являются предпочтительным способом доступа к API сервисов Play. См. раздел Доступ к API Google .

В этом руководстве показано, как вы можете:

  • Автоматически управляйте подключением к сервисам Google Play.
  • Выполняйте синхронные и асинхронные вызовы API к любому из сервисов Google Play.
  • Вручную управляйте подключением к сервисам Google Play в тех редких случаях, когда это необходимо. Дополнительную информацию см. в разделе Подключения, управляемые вручную .
Рис. 1. Иллюстрация, показывающая, как клиент Google API предоставляет интерфейс для подключения и выполнения вызовов к любым доступным сервисам Google Play, таким как Google Play Games и Google Drive.

Чтобы начать работу, вам необходимо сначала установить библиотеку сервисов Google Play (версия 15 или более поздняя) для вашего Android SDK. Если вы еще этого не сделали, следуйте инструкциям в разделе «Настройка SDK сервисов Google Play» .

Запустить автоматически управляемое соединение

После того как ваш проект будет связан с библиотекой сервисов Google Play, создайте экземпляр GoogleApiClient , используя API GoogleApiClient.Builder в методе onCreate() вашего действия. Класс GoogleApiClient.Builder предоставляет методы, позволяющие указать API Google, которые вы хотите использовать, и желаемые области действия OAuth 2.0. Вот пример кода, который создает экземпляр GoogleApiClient , который подключается к службе Google Drive:

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() и передайте API Wearable , чтобы ваш клиент мог корректно обрабатывать отсутствующий API. Дополнительную информацию см. в разделе Доступ к API носимых устройств .

Чтобы начать автоматически управляемое соединение, необходимо указать реализацию интерфейса OnConnectionFailedListener для получения неразрешимых ошибок соединения. Когда ваш автоматически управляемый экземпляр GoogleApiClient пытается подключиться к API Google, он автоматически отображает пользовательский интерфейс, чтобы попытаться исправить любые устранимые сбои подключения (например, если необходимо обновить службы Google Play). Если произойдет ошибка, которую невозможно устранить, вы получите вызов onConnectionFailed() .

Вы также можете указать дополнительную реализацию интерфейса ConnectionCallbacks , если вашему приложению необходимо знать, когда автоматически управляемое соединение устанавливается или приостанавливается. Например, если ваше приложение выполняет вызовы для записи данных в API Google, их следует вызывать только после вызова метода 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() . Ваше приложение может сразу начать отправлять запросы на чтение к API Google после создания GoogleApiClient , не дожидаясь завершения соединения.

Общайтесь с сервисами Google

После подключения ваш клиент может выполнять вызовы чтения и записи, используя API-интерфейсы конкретных служб, для которых авторизовано ваше приложение, как указано API-интерфейсами и областями, которые вы добавили в свой экземпляр GoogleApiClient .

Примечание. Прежде чем обращаться к определенным службам Google, вам может потребоваться зарегистрировать свое приложение в консоли разработчика Google. Инструкции см. в соответствующем руководстве по началу работы с используемым вами API, например Google Drive или Google Sign-In .

Когда вы выполняете запрос на чтение или запись с помощью 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. Например, ваше приложение может вызывать методы для чтения файла с Google Диска независимо от того, подключен ли ваш экземпляр GoogleApiClient . После установления соединения выполняются запросы на чтение, поставленные в очередь. Запросы на запись генерируют ошибку, если ваше приложение вызывает методы записи сервисов Google Play, когда ваш клиент Google API не подключен.

Использование асинхронных вызовов

Чтобы сделать запрос асинхронным, вызовите setResultCallback() для PendingResult и предоставьте реализацию интерфейса 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.
            // ...
        }
    });
}

Когда ваше приложение получает объект Result в обратном вызове onResult() , он доставляется как экземпляр соответствующего подкласса, указанного используемым API, например DriveApi.MetadataBufferResult .

Использование синхронных вызовов

Если вы хотите, чтобы ваш код выполнялся в строго определенном порядке (возможно, потому, что результат одного вызова необходим в качестве аргумента для другого), вы можете сделать свой запрос синхронным, вызвав await() для PendingResult . Это блокирует поток и возвращает объект Result после завершения запроса. Этот объект предоставляется как экземпляр соответствующего подкласса, указанного в используемом вами API, например DriveApi.MetadataBufferResult .

Поскольку вызов await() блокирует поток до тех пор, пока не будет получен результат, ваше приложение никогда не должно выполнять синхронные запросы к API Google в потоке пользовательского интерфейса. Ваше приложение может создать новый поток с помощью объекта 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
        // ...
    }
}

Доступ к API носимого устройства

Wearable API предоставляет канал связи для приложений, которые работают на портативных и носимых устройствах. API состоит из набора объектов данных, которые система может отправлять и синхронизировать, а также прослушивателей, которые уведомляют ваши приложения о важных событиях с помощью уровня данных. API носимого устройства доступен на устройствах под управлением Android 4.3 (уровень API 18) или выше, если носимое устройство подключено и на нем установлено сопутствующее приложение Wear OS .

Использование автономного API Wearable API

Если ваше приложение использует API Wearable, но не другие API Google, вы можете добавить этот API, вызвав метод addApi() . В следующем примере показано, как добавить Wearable API в экземпляр GoogleApiClient :

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

В ситуациях, когда API носимых устройств недоступен, запросы на подключение, включающие 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
    }
    // ...
}

Использование Wearable API с другими API Google

Если ваше приложение использует Wearable API в дополнение к другим API Google, вызовите метод addApiIfAvailable() и передайте Wearable API , чтобы проверить, доступен ли он. Вы можете использовать эту проверку, чтобы помочь вашему приложению корректно обрабатывать случаи, когда API недоступен.

В следующем примере показано, как получить доступ к API Wearable вместе с 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();

В приведенном выше примере GoogleApiClient может успешно подключиться к Google Диску без подключения к Wearable API, если он недоступен. После подключения экземпляра GoogleApiClient убедитесь, что Wearable API доступен, прежде чем выполнять вызовы API:

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

Игнорирование ошибок подключения API

Если вы вызываете addApi() , а GoogleApiClient не может успешно подключиться к этому API, вся операция подключения для этого клиента завершается неудачей и вызывает обратный вызов onConnectionFailed() .

Вы можете зарегистрировать игнорирование сбоя подключения API, используя addApiIfAvailable() . Если API, добавленный с помощью addApiIfAvailable() не может подключиться из-за неисправимой ошибки (например, API_UNAVAILABLE для Wear), этот API удаляется из вашего GoogleApiClient , и клиент продолжает подключаться к другим API. Однако если какое-либо соединение API завершается с ошибкой, которую можно исправить (например, намерение разрешения согласия OAuth), операция подключения клиента завершается неудачно. При использовании автоматически управляемого соединения GoogleApiClient попытается устранить такие ошибки, если это возможно. При использовании соединения, управляемого вручную, ConnectionResult , содержащий намерение разрешения, доставляется в обратный вызов onConnectionFailed() . Ошибки подключения API игнорируются только в том случае, если для сбоя нет разрешения и API был добавлен с помощью addApiIfAvailable() . Чтобы узнать, как реализовать обработку сбоев подключения вручную, см. раздел Обработка сбоев подключения .

Поскольку API, добавленные с помощью addApiIfAvailable() не всегда могут присутствовать в подключенном экземпляре GoogleApiClient , вам следует защищать вызовы этих API, добавляя проверку с помощью hasConnectedApi() . Чтобы узнать, почему конкретному API не удалось подключиться, хотя вся операция подключения для клиента завершилась успешно, вызовите getConnectionResult() и получите код ошибки из объекта ConnectionResult . Если ваш клиент вызывает API, когда он не подключен к клиенту, вызов завершается с кодом состояния API_NOT_AVAILABLE .

Если API, который вы добавляете с помощью addApiIfAvailable() требует одной или нескольких областей, добавьте эти области в качестве параметров в вызове метода addApiIfAvailable() , а не с помощью метода addScope() . Области, добавленные с помощью этого подхода, могут не запрашиваться, если соединение API не удалось до получения согласия OAuth, тогда как области, добавленные с помощью addScope() запрашиваются всегда.

Соединения, управляемые вручную

В большей части этого руководства показано, как использовать метод enableAutoManage для инициирования автоматически управляемого соединения с автоматически устраняемыми ошибками. Почти во всех случаях это лучший и простой способ подключения к API Google из вашего приложения Android. Однако в некоторых ситуациях вам может потребоваться использовать в своем приложении управляемое вручную соединение с API Google:

  • Чтобы получить доступ к API Google вне какой-либо деятельности или сохранить контроль над соединением API.
  • Настройка обработки и разрешения ошибок подключения

В этом разделе приведены примеры этих и других расширенных вариантов использования.

Запуск соединения, управляемого вручную

Чтобы инициировать управляемое вручную соединение с GoogleApiClient , необходимо указать реализацию интерфейсов обратного вызова ConnectionCallbacks и OnConnectionFailedListener . Эти интерфейсы получают обратные вызовы в ответ на асинхронный метод connect() , когда подключение к сервисам Google Play завершается успешно, сбой или приостанавливается.

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

При управлении соединением вручную вам нужно будет вызывать методы connect() и disconnect() в нужные моменты жизненного цикла вашего приложения. В контексте активности лучше всего вызывать connect() в методе onStart() вашей активности и disconnect() в методе onStop() вашей активности. Методы connect() и disconnect() вызываются автоматически при использовании автоматически управляемого соединения.

Если вы используете GoogleApiClient для подключения к API, требующим аутентификации, например Google Drive или Google Play Games, велика вероятность, что ваша первая попытка подключения завершится неудачно, и ваше приложение получит вызов onConnectionFailed() с ошибкой SIGN_IN_REQUIRED , поскольку учетная запись пользователя не было указано.

Обработка сбоев соединения

Когда ваше приложение получает вызов обратного вызова onConnectionFailed() , вам следует вызвать hasResolution() для предоставленного объекта ConnectionResult . Если он возвращает true, ваше приложение может запросить у пользователя немедленные действия по устранению ошибки, вызвав startResolutionForResult() для объекта ConnectionResult . В этой ситуации метод startResolutionForResult() ведет себя так же, как startActivityForResult() , и запускает действие, соответствующее контексту, которое помогает пользователю устранить ошибку (например, действие, которое помогает пользователю выбрать учетную запись).

Если hasResolution() возвращает false, ваше приложение должно вызвать GoogleApiAvailability.getErrorDialog() , передав код ошибки этому методу. Это возвращает Dialog , предоставленное службами Google Play, соответствующее ошибке. Диалоговое окно может просто содержать сообщение, объясняющее ошибку, или оно также может содержать действие для запуска действия, которое может устранить ошибку (например, когда пользователю необходимо установить более новую версию сервисов 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() , ваша активность получает обратный вызов onActivityResult() с кодом результата RESULT_OK . Затем ваше приложение может снова вызвать 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() во время предыдущей попытки устранить ошибку, вам необходимо сохранить логическое значение, которое отслеживает, пытается ли ваше приложение уже устранить ошибку.

Как показано в приведенном выше примере кода, ваше приложение должно устанавливать логическое значение true каждый раз, когда оно вызывает startResolutionForResult() или отображает диалоговое окно из GoogleApiAvailability.getErrorDialog() . Затем, когда ваше приложение получит RESULT_OK в обратном вызове onActivityResult() , установите для логического значения значение 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.