Dostęp do interfejsów API Google za pomocą GoogleApiClient (wycofany)

Możesz używać obiektu GoogleApiClient („Klient interfejsu API Google”) do uzyskiwania dostępu do interfejsów API Google udostępnianych w bibliotece usług Google Play (np. Logowanie przez Google, Gry i Dysk). Klient interfejsu Google API zapewnia wspólny punkt dostępu do usług Google Play i zarządza połączeniem sieciowym między urządzeniem użytkownika a poszczególnymi usługami Google.

Nowszy interfejs GoogleApi i jego implementacje są jednak łatwiejsze w użyciu i stanowią preferowany sposób uzyskiwania dostępu do interfejsów API usług Google Play. Zobacz artykuł Dostęp do interfejsów Google API.

Z tego przewodnika dowiesz się, jak:

  • automatycznie zarządzać połączeniem z Usługami Google Play;
  • Wykonuj wywołania synchroniczne i asynchroniczne interfejsu API do dowolnej usługi Google Play.
  • W rzadkich przypadkach, gdy będzie to konieczne, możesz ręcznie zarządzać połączeniem z usługami Google Play. Więcej informacji znajdziesz w artykule Połączenia zarządzane ręcznie.
Rysunek 1. Ilustracja pokazująca, jak interfejs Google API Client umożliwia nawiązywanie połączeń z dostępnymi usługami Google Play, takimi jak Google Play Games czy Dysk Google.

Aby rozpocząć, musisz najpierw zainstalować bibliotekę Usług Google Play (wersja 15 lub nowsza) dla pakietu SDK Androida. Jeśli nie zostało to jeszcze zrobione, wykonaj instrukcje w artykule Konfigurowanie pakietu SDK Usług Google Play.

Rozpoczynanie połączenia zarządzanego automatycznie

Po połączeniu projektu z biblioteką usług Google Play utwórz instancję interfejsu GoogleApiClient za pomocą interfejsów API GoogleApiClient.Builder w metodzie onCreate() aktywności. Klasa GoogleApiClient.Builder udostępnia metody, które umożliwiają określenie interfejsów API Google, których chcesz używać, oraz odpowiednich zakresów OAuth 2.0. Oto przykładowy kod, który tworzy instancję GoogleApiClient, która łączy się z usługą Dysk Google:

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

Do tego samego interfejsu GoogleApiClient możesz dodać wiele interfejsów API i zakresów, dodając dodatkowe wywołania do addApi()addScope().

Ważne: jeśli dodasz interfejs Wearable API wraz z innymi interfejsami API do GoogleApiClient, możesz napotkać błędy połączenia klienta na urządzeniach, na których nie jest zainstalowana aplikacja Wear OS. Aby uniknąć błędów połączenia, wywołaj metodę addApiIfAvailable() i przekaż interfejs Wearable API, aby umożliwić klientowi prawidłowe przetworzenie braku interfejsu API. Więcej informacji znajdziesz w artykule Dostęp do interfejsu Wearable API.

Aby rozpocząć automatycznie zarządzane połączenie, musisz określić implementację interfejsu OnConnectionFailedListener, aby otrzymywać nierozwiązywalne błędy połączenia. Gdy instancja GoogleApiClient zarządzana automatycznie spróbuje połączyć się z interfejsami API Google, automatycznie wyświetli interfejs użytkownika, aby spróbować naprawić wszelkie możliwe do rozwiązania problemy z połączeniem (na przykład jeśli usługi Google Play wymagają aktualizacji). Jeśli wystąpi błąd, którego nie można rozwiązać, otrzymasz telefon na numeronConnectionFailed().

Możesz też określić opcjonalną implementację interfejsu ConnectionCallbacks, jeśli aplikacja musi wiedzieć, kiedy połączenie zarządzane automatycznie zostało nawiązane lub zawieszone. Jeśli np. Twoja aplikacja wysyła dane do interfejsów API Google, powinna to robić dopiero po wywołaniu metody onConnected().

Oto przykładowa aktywność, która implementuje interfejsy wywołania zwrotnego i dodaje je do klienta interfejsu 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

        // ...
    }
}

Twoja instancja GoogleApiClient połączy się automatycznie po wywołaniu przez aktywność onStart() i rozłączy się po wywołaniu onStop(). Po utworzeniu GoogleApiClient aplikacja może od razu zacząć wysyłać żądania odczytu do interfejsów API Google bez oczekiwania na nawiązanie połączenia.

Komunikowanie się z usługami Google

Po nawiązaniu połączenia klient może wykonywać wywołania odczytu i zapisu za pomocą interfejsów API związanych z usługami, do których aplikacja ma uprawnienia, zgodnie z interfejsami API i uprawnieniami dodanymi do instancji GoogleApiClient.

Uwaga: zanim wykonasz wywołania do określonych usług Google, konieczne może być zarejestrowanie aplikacji w Google Developer Console. Instrukcje znajdziesz w odpowiednim przewodniku dla interfejsu API, którego używasz, np. Dysku Google lub logowania przez Google.

Gdy wykonujesz żądanie odczytu lub zapisu za pomocą GoogleApiClient, klient interfejsu API zwraca obiekt PendingResult, który reprezentuje żądanie. Dzieje się to natychmiast, zanim żądanie zostanie przekazane do usługi Google, którą wywołuje Twoja aplikacja.

Oto na przykład żądanie odczytania pliku z Dysku Google, który zawiera obiekt PendingResult:

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

Gdy aplikacja ma obiekt PendingResult, może określić, czy żądanie ma być obsługiwane jako wywołanie asynchroniczne czy synchroniczne.

Wskazówka: aplikacja może umieszczać w kole żądania odczytu, gdy nie jest połączona z usługami Google Play. Na przykład aplikacja może wywoływać metody odczytu pliku z Dysku Google niezależnie od tego, czy instancja GoogleApiClient jest już połączona. Po nawiązaniu połączenia w kolejce będą wykonywane żądania odczytu. Jeśli aplikacja wywołuje metody zapisu usług Google Play, a klient Google API nie jest połączony, żądania zapisu generują błąd.

Używanie wywołań asynchronicznych

Aby żądanie było asynchroniczne, wywołaj funkcję setResultCallback()PendingResult i zapewnij implementację interfejsu ResultCallback. Oto przykład żądania wykonywanego asynchronicznie:

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

Gdy aplikacja otrzyma obiekt Result w wywołaniu zwrotnym onResult(), zostanie on dostarczony jako instancja odpowiedniej podklasy określonej przez używany interfejs API, np. DriveApi.MetadataBufferResult.

Używanie wywołań synchronicznych

Jeśli chcesz, aby kod był wykonywany w ściśle określonej kolejności (np. dlatego, że wynik jednego wywołania jest potrzebny jako argument do innego), możesz uczynić żądanie synchronicznym, wywołując funkcję await() w funkcji PendingResult. Blokuje on wątek i zwraca obiekt Result po zakończeniu przetwarzania żądania. Ten obiekt jest dostarczany jako instancja odpowiedniej podklasy określonej przez interfejs API, którego używasz, np. DriveApi.MetadataBufferResult.

Wywołanie metody await() blokuje wątek do czasu otrzymania wyniku, dlatego aplikacja nie powinna nigdy wysyłać synchronicznych żądań do interfejsów Google API na wątku interfejsu użytkownika. Aplikacja może utworzyć nowy wątek za pomocą obiektu AsyncTask i użyć tego wątku do wysłania żądania synchronicznego.

Ten przykład pokazuje, jak wysłać żądanie pliku do Dysku Google jako wywołanie synchroniczne:

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

Dostęp do interfejsu Wearable API

Interfejs Wearable API udostępnia kanał komunikacji dla aplikacji działających na urządzeniach przenośnych i do noszenia. Interfejs API składa się z zestawu obiektów danych, które system może wysyłać i synchronizować, oraz odbiorników, które powiadamią Twoje aplikacje o ważnych zdarzeniach za pomocą warstwy danych. Interfejs Wearable API jest dostępny na urządzeniach z Androidem 4.3 (poziom interfejsu API 18) lub nowszym, gdy urządzenie noszące jest połączone, a na urządzeniu jest zainstalowana aplikacja towarzysząca Wear OS.

Korzystanie z interfejsu Wearable API w trybie samodzielnym

Jeśli Twoja aplikacja korzysta z interfejsu Wearable API, ale nie z innych interfejsów Google API, możesz dodać ten interfejs API, wywołując metodę addApi(). Ten przykład pokazuje, jak dodać interfejs Wearable API do instancji GoogleApiClient:

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

Jeśli interfejs Wearable API jest niedostępny, żądania połączenia, które zawierają Wearable API, kończą się błędem o kodzeAPI_UNAVAILABLE.

Ten przykład pokazuje, jak sprawdzić, czy interfejs Wearable API jest dostępny:

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

Korzystanie z interfejsu Wearable API w połączeniu z innymi interfejsami API Google

Jeśli Twoja aplikacja oprócz innych interfejsów API Google korzysta też z interfejsu Wearable API, wywołaj metodę addApiIfAvailable() i przekaż do niej parametr Wearable API, aby sprawdzić, czy jest on dostępny. Dzięki temu sprawdzeniu aplikacja może łagodnie reagować na sytuacje, gdy interfejs API jest niedostępny.

Ten przykład pokazuje, jak uzyskać dostęp do interfejsu Wearable APIDrive API:

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

W powyższym przykładzie usługa GoogleApiClient może połączyć się z Dyskiem Google bez łączenia z interfejsem Wearable API, jeśli jest niedostępny. Po połączeniu instancji GoogleApiClient sprawdź, czy interfejs Wearable API jest dostępny, zanim wykonasz wywołania interfejsu API:

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

Pomijanie błędów połączenia z interfejsem API

Jeśli wywołasz funkcję addApi(), a interfejs GoogleApiClient nie może nawiązać połączenia z tym interfejsem API, cała operacja połączenia dla tego klienta zakończy się niepowodzeniem i wywoła wywołanie zwrotne onConnectionFailed().

Aby zarejestrować błąd połączenia z interfejsem API, który ma być zignorowany, użyj parametru addApiIfAvailable(). Jeśli interfejs API dodany za pomocą interfejsu addApiIfAvailable() nie może nawiązać połączenia z powodu nieodwracalnego błędu (np. API_UNAVAILABLE w przypadku Wear), ten interfejs API zostanie usunięty z GoogleApiClient, a klient połączy się z innymi interfejsami API. Jeśli jednak jakiekolwiek połączenie z interfejsem API zakończy się niepowodzeniem z powodu błędu, który można naprawić (np. intencji rozwiązania problemu z wyrażeniem zgody OAuth), operacja połączenia klienta zakończy się niepowodzeniem. Jeśli używasz połączenia zarządzanego automatycznie, GoogleApiClient będzie próbować rozwiązać takie błędy, o ile to możliwe. W przypadku połączenia zarządzanego ręcznie do wywołania zwrotnego onConnectionFailed() jest wysyłana informacja ConnectionResult zawierająca intencja rozwiązania. Błędy połączenia interfejsu API są ignorowane tylko wtedy, gdy nie ma sposobu na ich rozwiązanie i interfejs API został dodany za pomocą elementu addApiIfAvailable(). Aby dowiedzieć się, jak zaimplementować ręczne obsługiwanie błędów połączenia, przeczytaj artykuł Zarządzanie błędami połączenia.

Interfejsy API dodane za pomocą funkcji addApiIfAvailable() mogą nie być zawsze dostępne w połączonym wystąpieniu GoogleApiClient, dlatego musisz chronić wywołania tych interfejsów API, dodając sprawdzanie za pomocą funkcji hasConnectedApi(). Aby dowiedzieć się, dlaczego połączenie z danym interfejsem API nie powiodło się, mimo że cała operacja połączenia została wykonana przez klienta, wywołaj funkcję getConnectionResult() i pobierz kod błędu z obiektu ConnectionResult. Jeśli klient wywoła interfejs API, gdy nie jest połączony z klientami, wywołanie zakończy się niepowodzeniem z kodem stanu API_NOT_AVAILABLE.

Jeśli interfejs API, który dodajesz za pomocą interfejsu addApiIfAvailable(), wymaga co najmniej 1 zakresu, dodaj te zakresy jako parametry w wywołaniu metody addApiIfAvailable(), a nie za pomocą metody addScope(). Zakresy dodane za pomocą tego podejścia mogą nie zostać zażądane, jeśli połączenie z interfejsem API nie powiedzie się przed uzyskaniem zgody OAuth, podczas gdy zakresy dodane za pomocą interfejsu addScope() są zawsze wymagane.

Połączenia zarządzane ręcznie

Większość tego przewodnika pokazuje, jak za pomocą metody enableAutoManage zainicjować połączenie zarządzane automatycznie z automatycznie rozwiązywanymi błędami. W prawie wszystkich przypadkach jest to najlepszy i najprostszy sposób na połączenie z interfejsami API Google z aplikacji na Androida. Czasami jednak warto użyć w aplikacji połączenia z interfejsami API Google zarządzanego ręcznie:

  • Aby uzyskać dostęp do interfejsów API Google poza aktywnością lub zachować kontrolę nad połączeniem z interfejsem API
  • Aby dostosować sposób obsługi i rozwiązywania błędów połączenia

W tej sekcji znajdziesz przykłady tych i innych zaawansowanych zastosowań.

Rozpoczynanie połączenia zarządzanego ręcznie

Aby zainicjować ręcznie zarządzane połączenie z GoogleApiClient, musisz określić implementację interfejsów wywołania zwrotnego ConnectionCallbacksOnConnectionFailedListener. Te interfejsy otrzymują wywołania zwrotne w odpowiedzi na asynchroniczną metodę connect(), gdy połączenie z usługami Google Play zakończy się powodzeniem, zakończy się niepowodzeniem lub zostanie zawieszone.

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

Aby ręcznie zarządzać połączeniem, musisz wywołać metody connect()disconnect() we właściwych momentach cyklu życia aplikacji. W kontekście aktywności zalecamy wywołanie metody connect() w metodzie onStart() aktywności i metody disconnect() w metodzie onStop() aktywności. Metody connect()disconnect() są wywoływane automatycznie podczas korzystania z połączenia zarządzanego automatycznie.

Jeśli używasz interfejsu GoogleApiClient do łączenia się z interfejsami API, które wymagają uwierzytelnienia, takimi jak Dysk Google czy Google Play Games, istnieje duże prawdopodobieństwo, że pierwsza próba połączenia się zakończy niepowodzeniem, a Twoja aplikacja otrzyma wywołanie onConnectionFailed() z błędem SIGN_IN_REQUIRED, ponieważ nie zostało określone konto użytkownika.

Obsługa błędów połączenia

Gdy aplikacja otrzyma wywołanie zwrotne onConnectionFailed(), powinna wywołać metodę hasResolution() obiektu ConnectionResult. Jeśli zwróci wartość true, aplikacja może poprosić użytkownika o natychmiastowe podjęcie działań w celu rozwiązania błędu, wywołując funkcję startResolutionForResult() obiektu ConnectionResult. W tej sytuacji metoda startResolutionForResult() działa tak samo jak metoda startActivityForResult(). Uruchamia ona działanie odpowiednie do kontekstu, które pomaga użytkownikowi naprawić błąd (np. działanie pomagające w wybraniu konta).

Jeśli hasResolution()zwraca wartość false, aplikacja powinna wywołać metodę GoogleApiAvailability.getErrorDialog(), przekazując jej kod błędu. Zwraca to obiekt Dialog udostępniany przez usługi Google Play, który jest odpowiedni dla danego błędu. Okno dialogowe może zawierać tylko komunikat wyjaśniający błąd lub też zawierać polecenie wykonania czynności, która może rozwiązać problem (np. gdy użytkownik musi zainstalować nowszą wersję Usług Google Play).

Na przykład metoda wywołania zwrotnego onConnectionFailed() powinna teraz wyglądać tak:

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

Gdy użytkownik zakończy dialog wyświetlany przez startResolutionForResult() lub odrzuci wiadomość wyświetlaną przez GoogleApiAvailability.getErrorDialog(), Twoja aktywność otrzyma wywołanie zwrotne z kodem wyniku RESULT_OK.onActivityResult() Aplikacja może wtedy ponownie wywołać funkcję connect(). Na przykład:

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

W powyższym kodzie prawdopodobnie zauważysz wyrażenie logiczne mResolvingError. Dzięki temu można śledzić stan aplikacji, gdy użytkownik usuwa błąd, aby uniknąć powtarzających się prób usuwania tego samego błędu. Na przykład podczas wyświetlania okna selektora kont, które ma pomóc użytkownikowi w rozwiązaniu błęduSIGN_IN_REQUIRED, użytkownik może obrócić ekran. Powoduje to ponowne wykonanie Twojej aktywności i ponowne wywołanie metody onStart(), która z kolei ponownie wywoła metodę connect(). Spowoduje to kolejne wywołanie funkcji startResolutionForResult(), która tworzy kolejne okno wyboru konta przed dotychczasowym.

Ta wartość logiczna spełnia swoje zadanie tylko wtedy, gdy jest zachowana w przypadku różnych instancji aktywności. W następnej sekcji wyjaśniamy, jak zachować stan obsługi błędów w aplikacji pomimo innych działań użytkownika lub zdarzeń występujących na urządzeniu.

Utrzymywanie stanu podczas rozwiązywania błędu

Aby uniknąć wykonywania kodu w onConnectionFailed(), gdy trwa poprzednia próba rozwiązania błędu, musisz zachować wartość logiczną, która śledzi, czy aplikacja próbuje już naprawić błąd.

Jak widać w przykładowym kodzie powyżej, aplikacja powinna ustawiać wartość logiczną na true za każdym razem, gdy wywołuje funkcję startResolutionForResult() lub wyświetla okno dialogowe z GoogleApiAvailability.getErrorDialog(). Gdy aplikacja otrzyma wywołanie zwrotne onActivityResult() z wartością RESULT_OK, ustaw wartość logiczną na false.

Aby śledzić wartość logiczną podczas ponownego uruchamiania aktywności (np. gdy użytkownik obróci ekran), zapisz ją w danych zapisanego wystąpienia aktywności za pomocą funkcji 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);
}

Następnie przywróć zapisany stan podczas onCreate():

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

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

Teraz możesz bezpiecznie uruchomić aplikację i ręcznie połączyć się z usługami Google Play.