الوصول إلى 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 APIs التي تريد استخدامها ونطاقات 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 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

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