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

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

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

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()، سيتلقّى نشاطك معاودة الاتصال التي تتضمّن رمز نتيجة RESULT_OK onActivityResult(). ويمكن لتطبيقك بعد ذلك الاتصال برقم 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.