الوصول إلى Google APIs باستخدام GoogleApiClient (متوقّف نهائيًا)

يمكنك استخدام GoogleApiClient ("برنامج Google API") الوصول إلى واجهات Google APIs المتوفّرة في مكتبة خدمات Google Play (مثل "تسجيل الدخول بحساب Google" و"الألعاب" وDrive). يوفّر عميل Google API نقطة دخول مشتركة إلى "خدمات Google Play" وإدارة الشبكة اتصال بين جهاز المستخدم وكل خدمة من خدمات Google.

مع ذلك، أصبح من الأسهل استخدام واجهة GoogleApi الجديدة وتنفيذها. استخدامها وهي الطريقة المفضّلة للوصول إلى واجهات برمجة تطبيقات "خدمات Play". راجع الوصول إلى Google APIs.

يوضّح هذا الدليل كيفية تنفيذ ما يلي:

  • يمكنك إدارة اتصالك بخدمات Google Play تلقائيًا.
  • تنفيذ طلبات بيانات متزامنة وغير متزامنة من واجهة برمجة التطبيقات إلى أي من خدمات Google Play
  • يمكنك إدارة اتصالك بخدمات Google Play يدويًا في هذه الحالات النادرة التي اللازمة. لمزيد من المعلومات، يُرجى الاطّلاع على الاتصالات المُدارة يدويًا.
الشكل 1: رسم توضيحي يوضّح طريقة توفير برنامج Google API واجهة للاتصال وإجراء مكالمات على أي من خدمات Google Play المتاحة، مثل "ألعاب Google Play" وGoogle Drive

للبدء، يجب أولاً تثبيت مكتبة خدمات Google Play (الإصدار 15 أو الإصدار الأحدث) لحزمة تطوير البرامج (SDK) لنظام التشغيل Android. اتّبِع التعليمات الواردة في إعداد حزمة تطوير البرامج (SDK) الخاصة "بخدمات Google Play"

بدء عملية ربط مُدارة تلقائيًا

بعد ربط مشروعك بمكتبة خدمات Google Play، يمكنك إنشاء مثيل GoogleApiClient باستخدام GoogleApiClient.Builder في ما يلي واجهات برمجة التطبيقات onCreate() . تشير رسالة الأشكال البيانية GoogleApiClient.Builder الفئة توفر طرقًا تتيح لك تحديد واجهات برمجة تطبيقات 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();

يمكنك إضافة واجهات برمجة تطبيقات متعددة ونطاقات متعددة لنفس GoogleApiClient من خلال إلحاق مكالمات إضافية بـ addApi() وaddScope().

ملاحظة مهمة: في حال إضافة Wearable API مع واجهات برمجة تطبيقات أخرى إلى GoogleApiClient، قد تواجه أخطاء في اتصال العميل على الأجهزة التي لا تعمل لم يتم تثبيت تطبيق Wear OS عليه. إلى لتجنُّب أخطاء الاتصال، عليك استدعاء طريقة addApiIfAvailable() وإعلامك. Wearable API للسماح لعميلك بمعالجة الطلبات غير المتوفّرة واجهة برمجة التطبيقات. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة الوصول إلى واجهة برمجة التطبيقات القابلة للارتداء.

لبدء اتصال مُدار تلقائيًا، يجب تحديد تنفيذ OnConnectionFailedListener لتلقي أخطاء اتصال لا يمكن حلها. عندما تتم إدارة حسابك تلقائيًا يحاول مثيل GoogleApiClient الاتصال بـ Google APIs، فسيتم تلقائيًا عرض واجهة المستخدم لمحاولة إصلاح أي إخفاقات في الاتصال قابلة للحل (على سبيل المثال، إذا يجب تحديث "خدمات Google Play"). إذا حدث خطأ لا يمكن تم حلها، ستتلقى مكالمة onConnectionFailed()

يمكنك أيضًا تحديد طريقة تنفيذ اختيارية لواجهة ConnectionCallbacks إذا كان تطبيقك بحاجة إلى معرفة وقت إنشاء اتصال مُدار تلقائيًا أو تعليقه. على سبيل المثال إذا يجري تطبيقك اتصالات لكتابة البيانات إلى واجهات برمجة تطبيقات 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(). يمكن لتطبيقك البدء على الفور في إنشاء قراءة الطلبات إلى Google APIs بعد إنشاء GoogleApiClient، بدون في انتظار اكتمال الاتصال.

الاتصال بخدمات Google

بعد الاتصال، يمكن للعميل إجراء اتصالات بالقراءة والكتابة باستخدام واجهات برمجة التطبيقات الخاصة بالخدمة من أجل الذي تم اعتماده في تطبيقك، على النحو المحدّد في واجهات برمجة التطبيقات والنطاقات التي أضفتها إلى مثيل GoogleApiClient.

ملاحظة: قبل إجراء مكالمات مع خدمات معيَّنة من Google، قد تحتاج أولاً إلى تسجيل من خلال Google Developer Console. للحصول على التعليمات، راجع طريقة الحصول على الخاص بواجهة برمجة التطبيقات التي تستخدمها، مثل Google Drive أو تسجيل الدخول باستخدام حساب Google:

عند تنفيذ طلب قراءة أو كتابة باستخدام GoogleApiClient، يعرض برنامج واجهة برمجة التطبيقات عنصر 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. بالنسبة على سبيل المثال، يمكن لتطبيقك استدعاء طرق لقراءة ملف من Google Drive بغض النظر عمّا إذا كان مثيل 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()، حيث يتم تسليمه كمثيل للفئة الفرعية المناسبة كما هو محدد من خلال واجهة برمجة التطبيقات التي تستخدمها، مثل DriveApi.MetadataBufferResult

استخدام المكالمات المتزامنة

إذا كنت تريد تنفيذ التعليمة البرمجية بترتيب محدد بدقة، ربما بسبب الحاجة كوسيطة إلى أخرى، فيمكنك جعل طلبك متزامنًا من خلال استدعاء await() في صفحة PendingResult سيؤدي هذا الإجراء إلى حظر سلسلة المحادثات. وتعرض الكائن Result عند إدخال اكتمال الطلب. يتم تسليم هذا الكائن كمثيل للفئة الفرعية المناسبة كما هو محدّد من خلال واجهة برمجة التطبيقات التي تستخدمها، على سبيل المثال DriveApi.MetadataBufferResult

لأنّه يتم الاتصال بالرقم await() يحظر سلسلة المحادثات إلى أن تصل النتيجة، يجب ألا يرسل تطبيقك طلبات متزامنة إلى Google APIs على سلسلة واجهة المستخدم يمكن لتطبيقك إنشاء سلسلة محادثات جديدة باستخدام عنصر 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

توفّر واجهة Wearable API قناة اتصال للتطبيقات التي يتم تشغيلها على الأجهزة المحمولة والقابلة للارتداء. تتألف واجهة برمجة التطبيقات من مجموعة من كائنات البيانات التي يمكن للنظام إرسالها ومزامنتها المستمعين الذين يرسلون إشعارات إلى تطبيقاتك بالأحداث المهمة باستخدام طبقة بيانات. تشير رسالة الأشكال البيانية تتوفر واجهة Wearable API على الأجهزة التي تعمل بالإصدار 4.3 من نظام التشغيل Android (المستوى 18 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث عندما توصيل جهاز قابل للارتداء وتطبيق التطبيق المصاحب لنظام التشغيل Wear OS تم تثبيته على الجهاز.

استخدام واجهة برمجة تطبيقات مستقلة قابلة للارتداء

إذا كان تطبيقك يستخدم Wearable API وليس واجهات Google APIs الأخرى، يمكنك إضافة واجهة برمجة التطبيقات هذه من خلال لاستدعاء طريقة addApi(). يوضح المثال التالي كيفية إضافة السمة Wearable API للمثيل من GoogleApiClient:

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

في الحالات التي لا تتوفر فيها واجهة برمجة التطبيقات القابلة للارتداء، يطلب الاتصال التي تتضمّن إخفاق 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 مع واجهات Google APIs الأخرى

إذا كان تطبيقك يستخدم Wearable API بالإضافة إلى واجهات Google APIs الأخرى، عليك طلب addApiIfAvailable() ثم انتقِل إلى Wearable 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 Drive بدون الاتصال بواجهة Wearable API إذا لم تكن متاحة بعد ربط GoogleApiClient مثلاً، تأكَّد من توفّر Wearable API قبل إجراء طلبات البيانات من واجهة برمجة التطبيقات:

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

تجاهل إخفاقات اتصال واجهة برمجة التطبيقات

في حال الاتصال بـ addApi() وتعذَّر على GoogleApiClient إجراء ما يلي: الاتصال بواجهة برمجة التطبيقات هذه بنجاح، فستفشل عملية الاتصال بالكامل لهذا العميل تؤدي إلى معاودة الاتصال onConnectionFailed().

يمكنك تسجيل تعذُّر اتصال واجهة برمجة التطبيقات ليتم تجاهله من خلال استخدام addApiIfAvailable() إذا تمت إضافة واجهة برمجة تطبيقات باستخدام تعذّر اتصال addApiIfAvailable() بسبب خطأ غير قابل للإصلاح (مثل API_UNAVAILABLE على Wear) سيتم إسقاط واجهة برمجة التطبيقات هذه من GoogleApiClient وينتقل العميل إلى الاتصال بواجهات برمجة تطبيقات أخرى. ومع ذلك، إذا فشل أي اتصال بواجهة برمجة التطبيقات مع ظهور خطأ قابل للاسترداد (مثل هدف حل موافقة OAuth)، يتعذّر إتمام عملية ربط العميل. فعندما باستخدام اتصال مُدار تلقائيًا سيحاول GoogleApiClient لحلّ هذه الأخطاء متى أمكن. عند استخدام اتصال مُدار يدويًا ConnectionResult الذي يتضمن هدفًا لحل تم إرساله إلى معاودة الاتصال onConnectionFailed(). واجهة برمجة التطبيقات لا يتم تجاهل إخفاقات الاتصال إلا إذا لم يكن هناك حل للإخفاق وتمّت إضافة واجهة برمجة التطبيقات مع addApiIfAvailable(). لمعرفة كيفية تنفيذ تعذُّر الاتصال اليدوي المعالجة، راجع معالجة إخفاقات الاتصال.

لأنّه تمت إضافة واجهات برمجة التطبيقات باستخدام قد لا يكون addApiIfAvailable() متاحًا دائمًا في الأجهزة المتصلة على سبيل المثال GoogleApiClient، عليك حماية عمليات الاستدعاء لواجهات برمجة التطبيقات هذه من خلال إضافة علامة اختيار. باستخدام hasConnectedApi(). لمعرفة سبب فشل اتصال واجهة برمجة تطبيقات معينة عند نجاح عملية الاتصال بالكامل للعميل، اتصل getConnectionResult() واحصل على رمز الخطأ من الكائن ConnectionResult. إذا كان العميل يستدعي واجهة برمجة تطبيقات في حين أنّها غير عند الاتصال بالعميل، فإن الاتصال يخفق مع API_NOT_AVAILABLE رمز الحالة.

إذا كانت واجهة برمجة التطبيقات التي تضيفها من خلال addApiIfAvailable() تتطلب واحدًا أو نطاقات أكثر، فأضِف هذه النطاقات كمعلمات في يتم استدعاء طريقة addApiIfAvailable() بدلاً من استخدام addScope(). قد لا يتم طلب النطاقات التي تمت إضافتها باستخدام هذا النهج في حال كانت واجهة برمجة التطبيقات. يتعذّر الاتصال قبل الحصول على موافقة OAuth، في حين أن النطاقات التي تتم إضافتها باستخدام يتم طلب addScope() دائمًا.

الاتصالات المُدارة يدويًا

يوضح لك الجزء الأكبر من هذا الدليل كيفية استخدام enableAutoManage لبدء اتصال مُدار تلقائيًا مع أخطاء تم حلّها تلقائيًا. على بُعد في كل الحالات، فهذه هي أفضل الطرق وأسهلها للاتصال بـ Google APIs من تطبيق Android. ومع ذلك، هناك بعض المواقف التي قد تحتاج فيها إلى استخدام الاتصال المُدار يدويًا بـ Google APIs في تطبيقك:

  • للدخول إلى Google APIs خارج نطاق النشاط أو الاحتفاظ بالتحكّم في واجهة برمجة التطبيقات اتصال
  • لتخصيص معالجة أخطاء الاتصال وحلها

يعرض هذا القسم أمثلة على حالات الاستخدام هذه وحالات الاستخدام المتقدّمة الأخرى.

بدء عملية ربط مُدارة يدويًا

لبدء عملية ربط مُدارة يدويًا بخدمة 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 للربط بواجهات برمجة التطبيقات التي تتطلّب ذلك المصادقة، مثل Google Drive أو ألعاب Google Play، فهناك فرصة جيدة ستتعذّر محاولة الاتصال الأولى وسيتلقّى تطبيقك مكالمة إلى onConnectionFailed() مع SIGN_IN_REQUIRED خطأ بسبب عدم تحديد حساب المستخدم.

التعامل مع حالات تعذُّر الاتصال

عندما يتلقّى تطبيقك مكالمة هاتفية على الرقم onConnectionFailed() معاودة الاتصال، عليك الاتصال بالرقم hasResolution(). على ConnectionResult المقدّم الخاص بك. إذا تم عرض القيمة "صحيح"، يمكن لتطبيقك أن يطلب من المستخدم اتّخاذ إجراء فوري لحل الخطأ من خلال جارٍ الاتصال بالرقم 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 يدويًا.