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

ניתן להשתמש באובייקט GoogleApiClient ("לקוח Google API") כדי לגשת לממשקי ה-API של Google שזמינים בספריית Google Play Services (כמו 'כניסה באמצעות חשבון 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 שיטת 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().

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

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

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

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

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

מכיוון שקריאה ל-await() חוסמת את ה-thread עד שמתקבלת התוצאה, האפליקציה שלכם אף פעם לא אמורה לשלוח בקשות סינכרוניות ל-Google APIs ב-thread של ממשק המשתמש. האפליקציה יכולה ליצור שרשור חדש באמצעות אובייקט 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
        // ...
    }
}

גישה ל-Wearable API

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

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

אם באפליקציה נעשה שימוש ב-Wearable API אבל לא בממשקי Google API אחרים, ניתן להוסיף את ה-API על ידי קריאה לשיטה 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
    }
    // ...
}

שימוש ב-Wearable 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 הזה, כל פעולת החיבור של הלקוח תיכשל ותפעיל את הקריאה החוזרת (callback) של onConnectionFailed().

אפשר לרשום כשל בחיבור ל-API שהמערכת תתעלם ממנו באמצעות addApiIfAvailable(). אם API שנוסף באמצעות addApiIfAvailable() לא מצליח להתחבר בגלל שגיאה שלא ניתנת לשחזור (כמו API_UNAVAILABLE ל-Wear), ה-API הזה מוסר מ-GoogleApiClient והלקוח ממשיך להתחבר לממשקי API אחרים. עם זאת, אם חיבור ל-API כלשהו נכשל ומוצגת שגיאה שניתן לשחזר (כמו כוונה לפתרון בקשת הסכמה ב-OAuth), פעולת ההתחברות של הלקוח תיכשל. כשמשתמשים בחיבור שמנוהל באופן אוטומטי, GoogleApiClient ינסה לפתור שגיאות כאלה כשהדבר יתאפשר. כשמשתמשים בחיבור שמנוהל באופן ידני, ConnectionResult עם כוונת פתרון מועבר אל הקריאה החוזרת (callback) של onConnectionFailed(). המערכת מתעלמת מכשלים בחיבור 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, צריך לציין הטמעה של ממשקי הקריאה החוזרת (callback) ConnectionCallbacks וגם OnConnectionFailedListener. הממשקים האלה מקבלים קריאות חוזרות (callback) בתגובה לשיטה connect() האסינכרונית כשהחיבור ל-Google Play Services מצליח, נכשל או מושעה.

    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, כי חשבון המשתמש לא צוין.

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

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

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