خدمات Google Play وأذونات وقت التشغيل

منذ نظام التشغيل Android 6.0 Marshmallow، يستخدم Android نموذج الأذونات الذي يبسط عملية تثبيت التطبيق والتحديث التلقائي. يتم طلب الأذونات في وقت التشغيل بدلاً من طلبها قبل تثبيت التطبيق. بالإضافة إلى ذلك، يمكن للمستخدمين اختيار رفض أذونات معيَّنة. ولمنح المستخدمين هذه المرونة، عليك التأكّد من أنّ تطبيقك يعمل على النحو المتوقّع عندما يفعّل المستخدم إذنًا معيّنًا أو يوقفه.

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

من الممارسات الجيدة إدارة توقعات المستخدم في ما يتعلق بتعيين الأذونات التي قد يتطلبها وقت التشغيل. ستساعدك أفضل الممارسات التالية في تجنب المشكلات المحتملة.

المتطلبات الأساسية

عليك تقديم بيان الأذونات في ملف AndroidManifest.xml. مثال:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

الإرشادات

التأكّد من الأذونات قبل طلب البيانات من واجهات برمجة التطبيقات

بعد الإعلان عن واجهات برمجة التطبيقات التي تريد استخدامها في ملف AndroidManifest.xml، تأكَّد من حصولك على الإذن المطلوب قبل طلب البيانات من واجهة برمجة التطبيقات. يمكنك إجراء ذلك باستخدام طريقة checkSelfPermission في ActivityCompat أو ContextCompat.

إذا عرضت المكالمة خطأ، هذا يعني أنه لن يتم منح الأذونات وعليك استخدام requestPermissions لطلبها. يتم عرض الرد على ذلك في معاودة الاتصال التي ستراها في الخطوة التالية.

مثال:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
  // Check Permissions Now
  ActivityCompat.requestPermissions(this,
      new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
      REQUEST_LOCATION);
} else {
  // permission has been granted, continue as usual
  Task<Location> locationResult = LocationServices
      .getFusedLocationProviderClient(this /** Context */)
      .getLastLocation();
}

تنفيذ معاودة الاتصال بإذن الطلب

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

public void onRequestPermissionsResult(int requestCode,
                                       String[] permissions,
                                       int[] grantResults) {
    if (requestCode == REQUEST_LOCATION) {
        if(grantResults.length == 1
           && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // We can now safely use the API we requested access to
            Task<Location> locationResult = LocationServices
                .getFusedLocationProviderClient(this /** Context */)
                .getLastLocation();
        } else {
            // Permission was denied or request was cancelled
        }
    }
}

إظهار سبب الإذن

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

في هذه الحالة، عليك الاتصال بـ shouldShowRequestPermissionRationale قبل الاتصال بـ requestPermissions. إذا كان يعرض القيمة "true"، يجب إنشاء جزء من واجهة المستخدم لعرض سياق إضافي للإذن.

على سبيل المثال، قد يبدو الرمز الخاص بك كما يلي:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
    // Check Permissions Now
    private static final int REQUEST_LOCATION = 2;

    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.ACCESS_FINE_LOCATION)) {
        // Display UI and wait for user interaction
    } else {
        ActivityCompat.requestPermissions(
            this, new String[]{Manifest.permission.LOCATION_FINE},
            ACCESS_FINE_LOCATION);
    }
} else {
    // permission has been granted, continue as usual
    Task<Location> locationResult = LocationServices
        .getFusedLocationProviderClient(this /** Context */)
        .getLastLocation();
}

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

إذا كان تطبيقك يستخدم GoogleApiClient الذي تم إيقافه نهائيًا، وعند طلب الوصول إلى connect()، تتحقق "خدمات Google Play" من حصوله على كل الأذونات اللازمة المطلوبة. يتعذّر تنفيذ connect() عندما لا تتوفّر أي مجموعات أذونات تحتاجها "خدمات Google Play" نفسها.

إذا تعذّر الاتصال بالرقم connect()، تأكَّد من أنّ تطبيقك يعالج إخفاق الاتصال بشكلٍ صحيح. إذا تفتقد خدمات Google Play الأذونات نفسها، يمكنك استدعاء startResolutionForResult() لبدء تدفق المستخدم لإصلاح هذه الأذونات.

مثال:

@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 GooglePlayServicesUtil.getErrorDialog()
        showErrorDialog(result.getErrorCode());
        mResolvingError = true;
    }
}

ستعرض طلبات البيانات من واجهة برمجة التطبيقات الأحدث المستندة إلى GoogleApi إما مربّع حوار تلقائيًا (إذا تم إنشاء مثيل للعميل باستخدام Activity) أو إشعار في لوحة النظام (إذا تم إنشاء مثيل للعميل باستخدام Context) يمكن للمستخدم النقر عليه لبدء هدف تحليل الأذونات. ستتم إضافة المكالمات إلى قائمة الانتظار وإعادة المحاولة بعد منح الإذن.