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

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

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

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

  • إدارة اتصالك بـ "خدمات 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 مع واجهات برمجة تطبيقات أخرى إلى GoogleApiClient، قد تواجه أخطاء في اتصال العميل على الأجهزة التي لم يتم تثبيت تطبيق Wear OS عليها. لتجنُّب أخطاء الاتصال، يمكنك استدعاء الطريقة addApiIfAvailable() وتقديم Wearable API للسماح لعميلك بمعالجة واجهة برمجة التطبيقات التي لا تعمل بشكل سلس. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة الوصول إلى Wearable API.

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

التواصل مع "خدمات Google"

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

ملاحظة: قبل إجراء مكالمات إلى خدمات معيّنة من Google، قد تحتاج أولاً إلى تسجيل تطبيقك في Google Developers 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 متصلاً أم لا. بعد إنشاء اتصال، يتم تنفيذ طلبات القراءة المُدرَجة في قائمة الانتظار. تؤدي طلبات الكتابة إلى ظهور خطأ إذا كان تطبيقك يستدعي methods كتابة خدمات Google Play عندما لا يكون برنامج Google API Client متصلاً.

استخدام طلبات غير متزامنة

لجعل الطلب غير متزامن، يمكنك الاتصال بواجهة 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 في callbackonResult()، يتم إرساله كمثيل للطبقة الفرعية المناسبة كما هو محدّد في واجهة برمجة التطبيقات التي تستخدمها، مثلDriveApi.MetadataBufferResult.

استخدام طلبات البيانات المتزامنة

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

وبما أنّ استدعاء await() يحظر سلسلة المحادثات إلى أن تصل النتيجة، يجب ألا يقدّم تطبيقك أبدًا طلبات متزامنة إلى واجهات برمجة تطبيقات Google في سلسلت المحادثات لواجهة المستخدم. يمكن لتطبيقك إنشاء سلسلة محادثات جديدة باستخدام عنصر AsyncTask، واستخدام هذه السلسلة لتقديم الطلب المتزامن.

يوضّح المثال التالي كيفية تقديم طلب ملف إلى Google Drive كطلب متزامن:

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

الوصول إلى واجهة برمجة التطبيقات Wearable API

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

استخدام Wearable API بشكل مستقل

إذا كان تطبيقك يستخدم Wearable API وليس واجهات برمجة تطبيقات Google الأخرى، يمكنك إضافة واجهة برمجة التطبيقات هذه من خلال استدعاء طريقة 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 مع واجهات برمجة تطبيقات Google الأخرى

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

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

تجاهل حالات تعذُّر الاتصال بواجهة برمجة التطبيقات

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

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

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

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

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

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

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

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

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

لبدء عملية اتصال مُدارة يدويًا بـ 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() تُعرِض قيمة خاطئة، يجب أن يستدعي تطبيقك 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 في 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".