الدخول إلى Google APIs باستخدام GoogleApiClient (متوقف)

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

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

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

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

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

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

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

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

يوضّح المثال التالي كيفية تحديد ما إذا كانت واجهة برمجة التطبيقات القابلة للارتداء متاحة:

// 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 API الأخرى

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

يوضّح المثال التالي كيفية الوصول إلى واجهة برمجة التطبيقات القابلة للارتداء بالإضافة إلى 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 for 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 API خارج النشاط أو الاحتفاظ بإمكانية التحكم في اتصال واجهة برمجة التطبيقات
  • لتخصيص معالجة أخطاء الاتصال وحلها

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

بدء اتصال مُدار يدويًا

لبدء اتصال مُدار يدويًا بـ 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.