גישה ל-Google APIs באמצעות GoogleApiClient (הוצא משימוש)

אפשר להשתמש באובייקט GoogleApiClient ("לקוח Google API") כדי לגשת לממשקי Google API שכלולים בספריית שירותי Google Play (כמו כניסה באמצעות חשבון Google, משחקים ו-Drive). לקוח Google API מספק נקודת כניסה משותפת לשירותי Google Play, ומנהל את חיבור הרשת בין המכשיר של המשתמש לבין כל אחד מהשירותים של Google.

עם זאת, קל יותר להשתמש בממשק GoogleApi החדש ובהטמעות שלו, והם הדרך המועדפת לגשת לממשקי ה-API של Play Services. למידע נוסף, ראו גישה ל-Google APIs.

במדריך הזה תלמדו:

  • ניהול אוטומטי של החיבור אל Google Play Services.
  • לבצע קריאות סינכרוניות ואסינכרוניות ל-API לכל אחד משירותי Google Play.
  • ניהול ידני של החיבור ל-Google Play Services במקרים הנדירים שבהם יש צורך בכך. למידע נוסף על חיבורים שמנוהלים באופן ידני
איור 1: איור שמראה איך לקוח Google API מספק ממשק לחיבור ולביצוע שיחות לכל אחד משירותי Google Play הזמינים, כמו Google Play Games ו-Google Drive.

כדי להתחיל, קודם צריך להתקין את ספריית Google Play Services (גרסה 15 ואילך) ל-Android SDK. אם עדיין לא עשיתם זאת, עליכם לפעול לפי ההוראות במאמר הגדרת Google Play Services SDK.

הפעלת חיבור שמנוהל באופן אוטומטי

אחרי שמקשרים את הפרויקט לספריית Google Play Services, יוצרים מכונה של GoogleApiClient באמצעות ממשקי ה-API של GoogleApiClient.Builder ב-method onCreate() של הפעילות. המחלקה GoogleApiClient.Builder כוללת methods שמאפשרות לציין את ממשקי ה-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().

חשוב: אם אתם מוסיפים את ה-API של Wearable יחד עם ממשקי API אחרים ל-GoogleApiClient, יכול להיות שיהיו שגיאות חיבור של הלקוח במכשירים שלא מותקנת בהם האפליקציה ל-Wear OS. כדי למנוע שגיאות חיבור, קוראים ל-method addApiIfAvailable() ומעבירים את ה-API של Wearable כדי לאפשר ללקוח לטפל באלגנטיות ב-API החסר. מידע נוסף זמין במאמר בנושא גישה ל-Wearable API.

כדי להתחיל חיבור שמנוהל באופן אוטומטי, צריך לציין הטמעה לממשק OnConnectionFailedListener כדי לקבל שגיאות חיבור שלא ניתן לפתור. כשהמכונה בניהול אוטומטי GoogleApiClient מנסה להתחבר ל-Google APIs, היא מציגה באופן אוטומטי את ממשק המשתמש כדי לנסות לתקן כשלים בחיבור שניתן לפתור (לדוגמה, אם צריך לעדכן את שירותי Google Play). אם מתרחשת שגיאה שלא ניתן לפתור, תקבלו קריאה ל-onConnectionFailed().

אפשר גם לציין הטמעה אופציונלית לממשק ConnectionCallbacks אם האפליקציה צריכה לדעת מתי נוצר או מושעה החיבור שמנוהל באופן אוטומטי. לדוגמה, אם האפליקציה מבצעת קריאות לכתיבת נתונים ב-Google APIs, צריך להפעיל אותן רק אחרי הקריאה ל-method 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(). האפליקציה יכולה להתחיל לשלוח בקשות קריאה ל-Google APIs באופן מיידי אחרי פיתוח GoogleApiClient, בלי לחכות להשלמת החיבור.

תקשורת עם שירותי Google

אחרי ההתחברות, הלקוח יכול לבצע קריאה וכתיבה לקריאות באמצעות ממשקי ה-API הספציפיים לשירות שהאפליקציה שלכם מורשית אליהם, כפי שצוין בממשקי ה-API ובהיקפים שהוספתם למכונה של GoogleApiClient.

הערה: לפני שמבצעים שיחות לשירותי Google ספציפיים, יכול להיות שתצטרכו לרשום את האפליקציה ב-Google Developer Console. להוראות, עיינו במדריך לתחילת העבודה של ה-API שבו אתם משתמשים, כמו Google Drive או כניסה באמצעות חשבון Google.

כשמבצעים בקשת קריאה או כתיבה באמצעות GoogleApiClient, לקוח ה-API מחזיר אובייקט PendingResult שמייצג את הבקשה. הפעולה הזו מתבצעת מיד, לפני שהבקשה נשלחת לשירות Google שאליו האפליקציה מתקשרת.

לדוגמה, הנה בקשה לקריאת קובץ מ-Google Drive שמספק אובייקט PendingResult:

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

אחרי שהאפליקציה כוללת אובייקט PendingResult, האפליקציה יכולה לציין אם הבקשה תטופל כשיחה אסינכרונית או כקריאה מסונכרנת.

טיפ: האפליקציה שלכם יכולה להוסיף בקשות קריאה לתור כשהיא לא מחוברת לשירותי Google Play. לדוגמה, האפליקציה שלכם יכולה לקרוא ל-methods כדי לקרוא קובץ מ-Google Drive גם אם המכונה GoogleApiClient עדיין מחוברת. לאחר יצירת חיבור, מופעלות בקשות קריאה שמופיעות בתור. בקשות כתיבה יוצרות שגיאה אם האפליקציה שולחת קריאה לשיטות הכתיבה של Google Play Services בזמן שהלקוח 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 בקריאה החוזרת (callback) של onResult(), הוא נשלח כמופע של מחלקה המשנה המתאימה כפי שצוין על ידי ה-API שבו אתם משתמשים, למשל DriveApi.MetadataBufferResult.

שימוש בשיחות סינכרוניות

אם אתם רוצים שהקוד יפעל לפי סדר מוגדר בלבד, אולי בגלל שהתוצאה של קריאה אחת נדרשת כארגומנט לאחר, תוכלו להפוך את הבקשה לסינכרונית על ידי קריאה ל-await() ב-PendingResult. הפעולה הזו חוסמת את השרשור ומחזירה את האובייקט Result בסיום הבקשה. האובייקט הזה מוצג כמופע של קטגוריית המשנה המתאימה כפי שצוין על ידי ה-API שבו אתם משתמשים, לדוגמה DriveApi.MetadataBufferResult.

קריאה ל-await() חוסמת את השרשור עד לקבלת התוצאה, ולכן האפליקציה אף פעם לא יכולה לשלוח בקשות סינכרוניות ל-Google APIs בשרשורים ב-UI. האפליקציה שלכם יכולה ליצור שרשור חדש באמצעות אובייקט AsyncTask ולהשתמש בשרשור הזה כדי ליצור את הבקשה הסנכרנת.

הדוגמה הבאה מראה איך לשלוח בקשת קובץ ל-Google Drive כקריאה מסונכרנת:

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 מורכב מקבוצה של אובייקטים של נתונים שהמערכת יכולה לשלוח ולסנכרן, ותזכורות שמתעדות את האפליקציות לאירועים חשובים באמצעות שכבת נתונים. Wearable API זמין במכשירים עם Android מגרסה 4.3 (רמת API 18) ואילך כשמחוברים מכשיר לביש והאפליקציה הנלווית של Wear OS מותקנת במכשיר.

שימוש ב-Wearable API באופן עצמאי

אם באפליקציה שלכם נעשה שימוש ב-Wearable API אבל לא ב-Google APIs אחרים, אפשר להוסיף את ה-API הזה באמצעות קריאה ל-method addApi(). הדוגמה הבאה מראה איך להוסיף את Wearable API למכונה של GoogleApiClient:

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

במצבים שבהם Wearable API לא זמין, בקשות ההתחברות שכוללות את Wearable 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
    }
    // ...
}

שימוש ב-API הלביש עם ממשקי API אחרים של Google

אם האפליקציה שלך משתמשת ב-Wearable API בנוסף לממשקי API אחרים של Google, צריך להפעיל את השיטה addApiIfAvailable() ולהעביר אותה ב-Wearable API כדי לבדוק אם הוא זמין. תוכלו להשתמש בבדיקה הזו כדי לעזור לאפליקציה לטפל בצורה חלקה במקרים שבהם ה-API לא זמין.

הדוגמה הבאה מציגה איך לגשת ל-Wearable API יחד עם Drive 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();

בדוגמה שלמעלה, ל-GoogleApiClient יש אפשרות להתחבר ל-Google Drive בלי להתחבר ל-Wearable API אם הוא לא זמין. אחרי שמחברים את המכונה של GoogleApiClient, חשוב לוודא ש-Wearable API זמין לפני שמבצעים את הקריאות ל-API:

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

התעלמות מכשלים בחיבור ל-API

אם קוראים ל-addApi() וה-GoogleApiClient לא מצליח להתחבר ל-API הזה, כל פעולת החיבור של הלקוח תיכשל ותפעיל את הקריאה החוזרת של onConnectionFailed().

באמצעות addApiIfAvailable() תוכלו לרשום שהמערכת תתעלם מכשל בחיבור ל-API. אם ממשק API שנוסף באמצעות addApiIfAvailable() לא מצליח להתחבר עקב שגיאה שאי אפשר לשחזר (כמו API_UNAVAILABLE ב-Wear), ה-API הזה מושמט מ-GoogleApiClient והלקוח ממשיך להתחבר לממשקי API אחרים. אבל אם חיבור כלשהו ל-API נכשל ומוצגת שגיאה שניתנת לשחזור (כמו Intent לפתרון הסכמה ב-OAuth), פעולת החיבור של הלקוח תיכשל. כשמשתמשים בחיבור שמנוהל באופן אוטומטי, GoogleApiClient ינסה לפתור את השגיאות האלה כשזה יתאפשר. כשמשתמשים בחיבור שמנוהל באופן ידני, נשלחת הקריאה החוזרת (callback) של onConnectionFailed() את האובייקט ConnectionResult שמכיל Intent לפתרון. המערכת תתעלם מכשלים בחיבור ה-API רק אם אין פתרון לכשל, וה-API נוסף עם addApiIfAvailable(). במאמר טיפול בכשלים בחיבור מוסבר איך משתמשים בטיפול ידני בכשלים בחיבור.

מכיוון שממשקי API שנוספו עם addApiIfAvailable() לא תמיד נמצאים במכונה המקושרת GoogleApiClient, צריך להגן על קריאות לממשקי ה-API האלה על ידי הוספת בדיקה באמצעות hasConnectedApi(). כדי לגלות למה API מסוים לא הצליח להתחבר כשפעולת החיבור בוצעה בהצלחה אצל הלקוח, קוראים לפונקציה getConnectionResult() ומקבלים את קוד השגיאה מהאובייקט ConnectionResult. אם הלקוח קורא ל-API כשהוא לא מחובר ללקוח, הקריאה תיכשל עם קוד הסטטוס API_NOT_AVAILABLE.

אם ב-API שמוסיפים דרך addApiIfAvailable() נדרש היקף אחד או יותר, מוסיפים את ההיקפים האלה כפרמטרים בקריאה ל-method addApiIfAvailable() במקום להשתמש בשיטה addScope(). יכול להיות שלא יישלחו בקשות להיקפים שנוספו באמצעות הגישה הזו אם החיבור ל-API ייכשל לפני קבלת ההסכמה ל-OAuth, אבל תמיד יישלחו בקשות להיקפים שנוספים עם addScope().

חיבורים שמנוהלים באופן ידני

ברוב המדריך הזה נסביר איך להשתמש בשיטה enableAutoManage כדי ליצור חיבור שמנוהל באופן אוטומטי עם שגיאות שנפתרו באופן אוטומטי. כמעט בכל המקרים, זו הדרך הטובה והקלה ביותר להתחבר ל-Google APIs מהאפליקציה ל-Android. עם זאת, יש מצבים מסוימים שבהם כדאי להשתמש בחיבור שמנוהל באופן ידני ל-Google APIs באפליקציה:

  • כדי לגשת ל-Google APIs מחוץ לפעילות או לשמור על שליטה בחיבור ה-API
  • כדי להתאים אישית את הטיפול בשגיאות חיבור והפתרון שלהן

בקטע הזה מפורטות דוגמאות לתרחישים כאלה ולתרחישים מתקדמים נוספים.

הפעלת חיבור שמנוהל באופן ידני

כדי להתחיל חיבור שמנוהל באופן ידני אל GoogleApiClient, צריך לציין הטמעה לממשקי הקריאה החוזרת, ConnectionCallbacks ו-OnConnectionFailedListener. הממשקים האלה מקבלים קריאות חוזרות (callback) בתגובה לשיטה האסינכרונית connect() כשהחיבור ל-Google Play Services מצליח, נכשל או מושעה.

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

כשמנהלים חיבור באופן ידני, צריך להפעיל את ה-methods connect() ו-disconnect() בנקודות הנכונות במחזור החיים של האפליקציה. בהקשר של פעילות, השיטה המומלצת היא לקרוא ל-connect() בשיטה onStart() ול-disconnect() בשיטה onStop() של הפעילות. כשמשתמשים בחיבור שמנוהל באופן אוטומטי, מתבצעת קריאה אוטומטית ל-methods connect() ו-disconnect().

אם אתם משתמשים ב-GoogleApiClient כדי להתחבר לממשקי API שדורשים אימות, כמו Google Drive או Google Play Games, יש סיכוי גבוה שניסיון החיבור הראשון ייכשל והאפליקציה תקבל קריאה ל-onConnectionFailed() עם השגיאה SIGN_IN_REQUIRED כי חשבון המשתמש לא צוין.

טיפול בכשלים בחיבור

כשהאפליקציה מקבלת קריאה לקריאה החוזרת (callback) של onConnectionFailed(), צריך להפעיל את הפקודה hasResolution() באובייקט ConnectionResult. אם היא מחזירה את הערך True, האפליקציה יכולה לבקש מהמשתמש לבצע פעולה מיידית כדי לפתור את השגיאה באמצעות קריאה לפונקציה startResolutionForResult() באובייקט ConnectionResult. השיטה startResolutionForResult() פועלת כמו startActivityForResult() במצב הזה, ומפעילה פעילות שמתאימה להקשר שעוזר למשתמש לפתור את השגיאה (למשל, פעילות שעוזרת למשתמש לבחור חשבון).

אם hasResolution() מחזירה False, האפליקציה צריכה לקרוא ל-GoogleApiAvailability.getErrorDialog() ולהעביר את קוד השגיאה לשיטה הזו. הפעולה הזו מחזירה Dialog שסופק על ידי Google Play Services שמתאים לשגיאה. בתיבת הדו-שיח יכולה פשוט להציג הודעה שמסבירה את השגיאה, או להציג פעולה להפעלת פעילות שיכולה לפתור את השגיאה (למשל, כשהמשתמש צריך להתקין גרסה חדשה יותר של Google Play Services).

לדוגמה, עכשיו שיטת הקריאה החוזרת 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, המשתמש יכול לסובב את המסך. הפעולה הזו יוצרת מחדש את הפעילות וגורמת לקריאה חוזרת ל-method onStart(), שבעקבותיה היא מפעילה שוב את הפקודה connect(). התוצאה תהיה קריאה נוספת ל-startResolutionForResult(), שתפיק תיבת דו-שיח נוספת של בורר החשבונות לפני תיבת הדו-שיח הקיימת.

ערך בוליאני זה משמש למטרה שלשמה הוא נועדו רק אם הוא ממשיך בכל המופעים של הפעילות. בקטע הבא מוסבר איך לשמור על מצב הטיפול בשגיאות של האפליקציה, למרות פעולות או אירועים אחרים של המשתמשים שמתרחשים במכשיר.

שמירה על המצב במהלך פתרון שגיאה

כדי להימנע מהרצת הקוד ב-onConnectionFailed() בזמן שמתקיים ניסיון קודם לפתרון שגיאה, צריך לשמור ערך בוליאני שעוקב אחר השאלה אם האפליקציה כבר מנסה לפתור שגיאה.

כפי שמוצג בדוגמת הקוד שלמעלה, האפליקציה שלכם צריכה להגדיר ערך בוליאני כ-true בכל פעם שהיא שולחת קריאה ל-startResolutionForResult() או מציגה את תיבת הדו-שיח של GoogleApiAvailability.getErrorDialog(). לאחר מכן, כשהאפליקציה מקבלת את הערך RESULT_OK בקריאה החוזרת (callback) של 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 Services.