تحسين فحص الرمز باستخدام التعليقات التوضيحية

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

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

يتيح Android مجموعة متنوعة من التعليقات التوضيحية من خلال مكتبة التعليقات التوضيحية في Jetpack. يمكنك الوصول إلى المكتبة من خلال حزمة androidx.annotation.

ملاحظة: إذا كانت الوحدة تبعية إلى معالج تعليقات توضيحية، يجب استخدام إعدادات التبعية kapt أو ksp للغة Kotlin، أو إعدادات التبعية annotationProcessor لـ Java لإضافة هذه التبعية.

إضافة تعليقات توضيحية إلى مشروعك

لتفعيل التعليقات التوضيحية في مشروعك، أضِف التبعية androidx.annotation:annotation إلى مكتبتك أو تطبيقك. ويتم التحقّق من أي تعليقات توضيحية تضيفها عند تنفيذ فحص код أو مهمة lint.

إضافة تبعية مكتبة التعليقات التوضيحية في Jetpack

يتم نشر مكتبة Jetpack Annotations على مستودع Maven من Google. لإضافة مكتبة Jetpack Anotations إلى مشروعك، أدرِج السطر التالي في كتلة dependencies من ملف build.gradle أوملف build.gradle.kts:

Kotlin

dependencies {
    implementation("androidx.annotation:annotation:1.9.1")
}

رائع

dependencies {
    implementation 'androidx.annotation:annotation:1.9.1'
}
بعد ذلك، انقر على المزامنة الآن في شريط الأدوات أو إشعار المزامنة الذي يظهر.

إذا كنت تستخدم تعليقات توضيحية في وحدة مكتبتك، يتم تضمين التعليقات التوضيحية كجزء من ملف annotations.zip بتنسيق XML ضمن ملف ملف أرشيف Android (AAR). لا تؤدي إضافة تبعية androidx.annotation إلى تقديم اعتمادية لأي مستخدمين في مرحلة ما بعد الإنتاج في مكتبتك.

ملاحظة: إذا كنت تستخدم مكتبات Jetpack أخرى، قد لا تحتاج إلى إضافة التبعية androidx.annotation. بما أنّ العديد من مكتبات Jetpack الأخرى تعتمد على مكتبة التعليقات التوضيحية، قد يكون بإمكانك الوصول إلى التعليقات التوضيحية.

للحصول على قائمة كاملة بالتعليقات التوضيحية المضمّنة في مستودع Jetpack، يمكنك الاطّلاع على مرجع مكتبة التعليقات التوضيحية في Jetpack أو استخدام ميزة الإكمال التلقائي لعرض الخيارات المتاحة لعبارة import androidx.annotation..

تنفيذ عمليات فحص الرموز البرمجية

لبدء فحص الرمز من Android Studio، والذي يتضمّن التحقّق من التعليقات التوضيحية وفحص lint التلقائي، اختَر تحليل > فحص الرمز من الجدول. يعرض "استوديو Android" رسائل التعارضات للإبلاغ عن المشاكل المحتملة حيث يتعارض الرمز البرمجي مع التعليقات التوضيحية واقتراح حلول ممكنة.

يمكنك أيضًا فرض التعليقات التوضيحية من خلال تشغيل مهمة lint باستخدام سطر الأوامر. على الرغم من أنّ هذا قد يكون مفيدًا للإبلاغ عن المشاكل باستخدام خادم دمج مستمر، لا تفرض مهمة lint استخدام التعليقات التوضيحية للقيمة الخالية (الموضَّحة في القسم التالي)، بل ينفّذ Android Studio ذلك فقط. للاطّلاع على مزيد من المعلومات حول تفعيل فحوصات Lint وتشغيلها، يُرجى الاطّلاع على تحسين الترميز باستخدام عمليات فحص الوبر.

على الرغم من أنّ تعارضات التعليقات التوضيحية تؤدي إلى ظهور تحذيرات، لا تمنع هذه التحذيرات compilingتطبيقك.

تعليقات توضيحية فارغة

يمكن أن تكون التعليقات التوضيحية حول القيمة الخالية مفيدة في رمز Java لفرض ما إذا كان يمكن أن تكون القيم خالية. وهي أقل فائدة في رمز Kotlin، لأنّ Kotlin تتضمّن قواعد إمكانية قبول القيم الفارغة التي يتم فرضها في وقت الترجمة.

أضِف التعليقَين التوضيحيَين @Nullable و @NonNull للتحقّق من قيمة فارغة لمتغيّر أو مَعلمة أو قيمة معروضة. يشير التعليق التوضيحي @Nullable إلى متغيّر أو مَعلمة أو قيمة إرجاع يمكن أن تكون فارغة. يشير الرمز @NonNull إلى متغيّر أو مَعلمة أو قيمة معروضة لا يمكن أن تكون فارغة.

على سبيل المثال، إذا تم إدخال متغيّر محلي يحتوي على قيمة فارغة كمَعلمة إلى طريقة تتضمّن تعليق @NonNull التوضيحي مرفقًا بتلك المَعلمة، سيؤدي إنشاء الرمز البرمجي إلى ظهور تحذير يشير إلى وجود تعارض غير فارغ. بالإضافة إلى ذلك، عند محاولة الإشارة إلى نتيجة طريقة تم وضع علامة @Nullable عليها بدون التحقّق أولاً مما إذا كانت النتيجة خالية، يتم عرض تحذير بعدم صحة القيم. لا تستخدِم @Nullable إلا على قيمة الإرجاع الخاصة بطريقة إذا كان يجب التحقّق صراحةً من عدم تساوي كل استخدام للطريقة مع القيمة الخالية.

يوضح المثال التالي قابلية القيم الفارغة عمليًا. لا يستفيد مثال الرمز البرمجي بلغة Kotlin من التعليق التوضيحي @NonNull لأنّه تتم إضافته تلقائيًا إلى رمز البايت الذي تم إنشاؤه عند تحديد نوع غير قابل للقيم الفارغة. يستفيد مثال Java من التعليق التوضيحي @NonNull على المَعلمتَين context وattrs للتحقّق من أنّ قيم المَعلمات المُرسَلة ليست فارغة. وتتحقّق أيضًا من أنّ الطريقة onCreateView() نفسها لا تعرض قيمة فارغة:

Kotlin

...
    /** Annotation not used because of the safe-call operator(?)**/
    override fun onCreateView(
            name: String?,
            context: Context,
            attrs: AttributeSet
    ): View? {
        ...
    }
...

Java

import androidx.annotation.NonNull;
...
    /** Add support for inflating the <fragment> tag. **/
    @NonNull
    @Override
    public View onCreateView(String name, @NonNull Context context,
      @NonNull AttributeSet attrs) {
      ...
      }
...

تحليل القيم الفارغة

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

  • استدعاء طرق يمكنها عرض قيمة فارغة
  • الطرق التي يجب ألا تُعرِض قيمة فارغة
  • المتغيّرات، مثل الحقول والمتغيّرات المحلية والمعلَمات التي يمكن أن تكون فارغة.
  • المتغيّرات، مثل الحقول والمتغيّرات المحلية والمَعلمات، التي لا يمكنها احتواء قيمة فارغة

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

لإجراء تحليل للقيمة الخالية في "استوديو Android"، اختَر تحليل > استنتاج القيمة الخالية. يُدرج "استوديو Android" التعليقات التوضيحية @Nullable و@NonNull لنظام التشغيل Android في المواقع التي تم رصدها في الرمز. بعد إجراء تحليل للقيم الخالية، من الممارسات الجيدة التحقّق من التعليقات التوضيحية المُدرَجة.

ملاحظة: عند إضافة تعليقات توضيحية للقيم الفارغة، قد تقترح ميزة الإكمال التلقائي التعليقات التوضيحية من IntelliJ @Nullable و@NotNull بدلاً من التعليقات التوضيحية الفارغة في Android، وقد تستورد المكتبة المقابلة تلقائيًا. ومع ذلك، لا يبحث مدقّق الأخطاء lint في "استوديو Android" سوى عن التعليقات التوضيحية للقيم الخالية في Android. عند التحقّق من تعليقاتك التوضيحية، تأكَّد من أنّ مشروعك يستخدم التعليقات التوضيحية الفارغة في Android حتى تتمكّن أداة فحص الوبر من إشعارك بشكل صحيح أثناء فحص الرمز.

التعليقات التوضيحية للموارد

قد يكون من المفيد التحقق من أنواع الموارد لأنّه يتم تمرير مراجع Android إلى الموارد، مثل الموارد القابلة للرسم والسلسلة، كأعداد صحيحة.

إنّ الرمز الذي يتوقع معلَمة أن تشير إلى نوع معيّن من الموارد، مثل String، يمكن تمريره إلى نوع المرجع المتوقع int، ولكنه يشير في الواقع إلى نوع مختلف من الموارد، مثل مورد R.string.

على سبيل المثال، أضِف تعليقات توضيحية @StringRes لمحاولة معرفة ما إذا كانت مَعلمة المورد تحتوي على مرجع R.string، كما هو موضّح هنا:

Kotlin

abstract fun setTitle(@StringRes resId: Int)

Java

public abstract void setTitle(@StringRes int resId)

أثناء فحص الرمز، يُنشئ التعليق التوضيحي تحذيرًا إذا لم يتم تمرير مرجع R.string في المَعلمة.

يمكن إضافة تعليقات توضيحية لأنواع الموارد الأخرى، مثل @DrawableRes و@DimenRes و@ColorRes و@InterpolatorRes، باستخدام تنسيق التعليق التوضيحي نفسه، ويمكن تنفيذها أثناء فحص الرمز البرمجي.

إذا كانت المَعلمة تتوافق مع أنواع موارد متعدّدة، يمكنك وضع أكثر من تعليق توضيحي لنوع واحد من الموارد على مَعلمة معيّنة. استخدِم @AnyRes للإشارة إلى أنّ المَعلمة المُشارَك فيها تعليقات توضيحية يمكن أن تكون أيّ نوع من موارد R.

مع أنّه يمكنك استخدام @ColorRes لتحديد أنّ المَعلمة يجب أن تكون مورد لون، لا يتم التعرّف على العدد الصحيح للّون (بتنسيق RRGGBB أو AARRGGBB) كمورد لوني. بدلاً من ذلك، استخدِم التعليق التوضيحي @ColorInt للإشارة إلى أنّ المَعلمة يجب أن تكون عددًا صحيحًا للّون. ستبلغ أدوات التصميم عن الرمز غير الصحيح الذي يمرر معرِّف مورد لون مثل android.R.color.black بدلاً من عدد صحيح للون كطرق تتضمن تعليقات توضيحية.

التعليقات التوضيحية لسلسلة المحادثات

تتحقّق التعليقات التوضيحية لسلسلة المحادثات ممّا إذا تم استدعاء طريقة من نوع معيّن من سلسلة محادثات. يمكن استخدام التعليقات التوضيحية التالية في السلسلة:

تتعامل أدوات التصميم مع التعليق التوضيحي @MainThread و@UiThread على أنّه قابل للتبديل، لذا يمكنك استدعاء طريقتَي @UiThread من طُرق @MainThread والعكس صحيح. ومع ذلك، من المحتمل أن تكون سلسلة واجهة المستخدم مختلفة عن سلسلة التعليمات الرئيسية، في حال تطبيقات النظام ذات طرق العرض المتعددة على سلاسل محادثات مختلفة. لذلك، عليك إضافة تعليقات توضيحية إلى الطرق المرتبطة بالعرض الهرمي لعرض التطبيق باستخدام @UiThread وإضافة تعليقات توضيحية إلى الطرق المرتبطة بدورة حياة التطبيق فقط باستخدام @MainThread.

إذا كانت جميع الطرق في فئة تتشارك متطلبات المعالجة المتعدّدة ال threads نفسها، يمكنك إضافة تعليق توضيحي واحد لسلسلة رسائل إلى الفئة للتحقّق من أنّه يتم استدعاء جميع الطرق في الفئة من النوع نفسه من سلسلة الرسائل.

من الاستخدامات الشائعة للتعليقات التوضيحية لسلاسل المحادثات التأكد من أنّ الطرق أو الفئات التي تحتوي على تعليقات توضيحية باستخدام @WorkerThread لا يتم استدعاؤها إلا من سلسلة محادثات مناسبة في الخلفية.

التعليقات التوضيحية لقيود القيمة

استخدِم التعليقات التوضيحية @IntRange و@FloatRange و@Size للتحقّق من صحة قيم المعلَمات التي تم تمريرها. يكون كلّ من @IntRange و@FloatRange مفيدًا للغاية عند تطبيقهما على المَعلمات التي يُرجّح أن يخطئ فيها المستخدِمون في النطاق.

يتحقق التعليق التوضيحي @IntRange من أنّ قيمة المَعلمة الكاملة أو الطويلة ضمن نطاق محدّد. يشير المثال التالي إلى أنّ المَعلمة alpha يجب أن تحتوي على قيمة عدد صحيح تتراوح بين 0 و255:

Kotlin

fun setAlpha(@IntRange(from = 0, to = 255) alpha: Int) { ... }

Java

public void setAlpha(@IntRange(from=0,to=255) int alpha) { ... }

يتحقّق التعليق التوضيحي @FloatRange مما إذا كانت قيمة المَعلمة المتغيرة أو المَعلمة المتغيرة المزدوجة ضمن نطاق محدّد من قيم النقطة العائمة. يشير المثال التالي إلى أنّ مَعلمة alpha يجب أن تحتوي على قيمة عائمة تتراوح بين 0.0 و1.0:

Kotlin

fun setAlpha(@FloatRange(from = 0.0, to = 1.0) alpha: Float) {...}

Java

public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {...}

يتحقّق التعليق التوضيحي @Size من حجم مجموعة أو صفيف أو طول سلسلة. يمكن استخدام التعليق التوضيحي @Size للتحقّق من الصفات التالية:

  • الحد الأدنى للحجم، مثل @Size(min=2)
  • الحد الأقصى للحجم، مثل @Size(max=2)
  • الحجم الدقيق، مثل @Size(2)
  • رقم يجب أن يكون حجم الصورة مضاعِفًا له، مثل @Size(multiple=2)

على سبيل المثال، يتحقّق @Size(min=1) من أنّ المجموعة ليست فارغة، ويتحقّق @Size(3) من أنّ الصفيف يحتوي على ثلاث قيم بالضبط.

يشير المثال التالي إلى أنّ مصفوفة location يجب أن تحتوي على عنصر واحد على الأقل:

Kotlin

fun getLocation(button: View, @Size(min=1) location: IntArray) {
    button.getLocationOnScreen(location)
}

Java

void getLocation(View button, @Size(min=1) int[] location) {
    button.getLocationOnScreen(location);
}

التعليقات التوضيحية للأذونات

يمكنك استخدام التعليق التوضيحي @RequiresPermission للتحقق من أذونات المتصل بطريقة ما. للتحقق من إذن واحد من قائمة بالأذونات الصالحة، استخدِم السمة anyOf. للتحقّق من توفُّر مجموعة من الأذونات، استخدِم السمة allOf. يوضّح المثال التالي تعليقًا توضيحيًا على الإجراء setWallpaper() للإشارة إلى أنّه يجب أن يكون لدى المُرسِل للإجراء إذن permission.SET_WALLPAPERS:

Kotlin

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
@Throws(IOException::class)
abstract fun setWallpaper(bitmap: Bitmap)

Java

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;

في المثال التالي، يجب أن يكون لدى المتصل الذي يستخدم الطريقة copyImageFile() إذن بالوصول للقراءة إلى وحدة التخزين الخارجية وإذن قراءة البيانات الوصفية للموقع في الصورة المنسوخة:

Kotlin

@RequiresPermission(allOf = [
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.ACCESS_MEDIA_LOCATION
])
fun copyImageFile(dest: String, source: String) {
    ...
}

Java

@RequiresPermission(allOf = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.ACCESS_MEDIA_LOCATION})
public static final void copyImageFile(String dest, String source) {
    //...
}

بالنسبة إلى الأذونات في intent، يمكنك وضع متطلبات الإذن في حقل السلسلة الذي يُحدِّد اسم إجراء النية:

Kotlin

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
const val ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"

Java

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";

بالنسبة إلى أذونات مقدّمي المحتوى الذين يحتاجون إلى أذونات منفصلة للوصول إلى القراءة والكتابة، يجب تضمين كلّ شرط من شروط الأذونات في تعليق توضيحي @RequiresPermission.Read أو @RequiresPermission.Write:

Kotlin

@RequiresPermission.Read(RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(RequiresPermission(WRITE_HISTORY_BOOKMARKS))
val BOOKMARKS_URI = Uri.parse("content://browser/bookmarks")

Java

@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");

الأذونات غير المباشرة

عندما يعتمد الإذن على القيمة المحدّدة المقدَّمة لمَعلمة إحدى الطرق، استخدِم @RequiresPermission على المَعلمة نفسها بدون إدراج الأذونات المحدّدة. على سبيل المثال، تستخدم الطريقة startActivity(Intent) إذنًا غير مباشر على النية التي تم تمريرها إلى الطريقة:

Kotlin

abstract fun startActivity(@RequiresPermission intent: Intent, bundle: Bundle?)

Java

public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle)

عند استخدام أذونات غير مباشرة، تُجري أدوات الإنشاء تحليلاً لتدفق البيانات للتحقق مما إذا كانت الوسيطة التي تم تمريرها إلى الطريقة تحتوي على أي تعليقات @RequiresPermission توضيحية. وبعد ذلك، تفرض أي تعليقات توضيحية حالية من المَعلمة على الطريقة نفسها. في مثال startActivity(Intent)، تؤدي التعليقات التوضيحية في فئة Intent إلى ظهور التحذيرات الناتجة عن الاستخدامات غير الصالحة startActivity(Intent) عند تمرير نية بدون الأذونات المناسبة إلى الطريقة، كما هو موضّح في الشكل 1.

الشكل 1: التحذير الذي تم إنشاؤه من تعليق توضيحي غير مباشر للأذونات في طريقة startActivity(Intent)

تنشئ أدوات التصميم التحذير على startActivity(Intent) من التعليق التوضيحي على اسم إجراء intent المقابل في الفئة Intent:

Kotlin

@RequiresPermission(Manifest.permission.CALL_PHONE)
const val ACTION_CALL = "android.intent.action.CALL"

Java

@RequiresPermission(Manifest.permission.CALL_PHONE)
public static final String ACTION_CALL = "android.intent.action.CALL";

يمكنك استبدال @RequiresPermission ب @RequiresPermission.Read أو @RequiresPermission.Write عند التعليق التوضيحي لمَعلمة إحدى الطرق، إذا لزم الأمر. ومع ذلك، بالنسبة إلى الأذونات غير المباشرة، يجب عدم استخدام @RequiresPermission مع التعليقات التوضيحية لأذونات القراءة أو الكتابة.

التعليقات التوضيحية للقيمة المعروضة

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

على سبيل المثال، غالبًا ما يظن مطوّرو Java الجدد عن طريق الخطأ أنّ <String>.trim() يزيل المسافات البيضاء من السلسلة الأصلية. إن التعليق التوضيحي على الطريقة باستخدام علامات @CheckResult يستخدم <String>.trim() حيث لا يفعل المتصل أي شيء بالقيمة التي تعرضها الطريقة.

يوضِّح المثال التالي تعليقًا توضيحيًا على الطريقة checkPermissions() للتحقّق مما إذا كان يتمّ إشارة فعليًا إلى القيمة المعروضة في طريقة الإخراج. وهي تسمي أيضًا الطريقة enforcePermission() كطريقة يتم اقتراحها على المطوّر كبديل:

Kotlin

@CheckResult(suggest = "#enforcePermission(String,int,int,String)")
abstract fun checkPermission(permission: String, pid: Int, uid: Int): Int

Java

@CheckResult(suggest="#enforcePermission(String,int,int,String)")
public abstract int checkPermission(@NonNull String permission, int pid, int uid);

التعليقات التوضيحية في CallSuper

استخدِم التعليق التوضيحي @CallSuper لتأكيد أنّ الطريقة التي تلغي طريقة أخرى تستدعي تنفيذ الطريقة الأصلية.

يُعلِق المثال التالي onCreate() تعليقًا توضيحيًا لضمان أنّ أي تنفيذات لطريقة super.onCreate() تُطلِق super.onCreate():

Kotlin

@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
}

Java

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

التعليقات التوضيحية Typedef

تتحقّق التعليقات التوضيحية لنوع البيانات من ما إذا كانت مَعلمة معيّنة أو قيمة معروضة أو حقل معيّن يشير إلى مجموعة معيّنة من الثوابت. وتتيح هذه العناصر أيضًا ميزة "إكمال الرموز البرمجية" لتلقائيًا عرض الثوابت المسموح بها.

استخدِم التعليقات التوضيحية @IntDef و @StringDef لإنشاء تعليقات توضيحية مُدرَجة لمجموعات الأعداد الصحيحة والسلاسل من أجل التحقّق من صحة أنواع أخرى من مراجع الرموز البرمجية.

تستخدِم التعليقات التوضيحية لأنواع البيانات @interface للإعلان عن نوع التعليق التوضيحي الجديد المُدرَج. يُعدّ التعليقان التوضيحيان @IntDef و@StringDef، بالإضافة إلى @Retention، تعليقَين توضيحيَين للتعليق التوضيحي الجديد، وهما ضروريان لتحديد نوع المُدرَج. يطلب التعليق التوضيحي @Retention(RetentionPolicy.SOURCE) من المحول البرمجي عدم تخزين بيانات التعليقات التوضيحية العددية في ملف .class.

يوضّح المثال التالي خطوات إنشاء تعليق توضيحي يتحقّق مما إذا كانت القيمة التي تم تمريرها كمَعلمة طريقة تشير إلى إحدى الثوابت المحدّدة:

Kotlin

import androidx.annotation.IntDef
//...
// Define the list of accepted constants and declare the NavigationMode annotation.
@Retention(AnnotationRetention.SOURCE)
@IntDef(NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS)
annotation class NavigationMode

// Declare the constants.
const val NAVIGATION_MODE_STANDARD = 0
const val NAVIGATION_MODE_LIST = 1
const val NAVIGATION_MODE_TABS = 2

abstract class ActionBar {

    // Decorate the target methods with the annotation.
    // Attach the annotation.
    @get:NavigationMode
    @setparam:NavigationMode
    abstract var navigationMode: Int

}

Java

import androidx.annotation.IntDef;
//...
public abstract class ActionBar {
    //...
    // Define the list of accepted constants and declare the NavigationMode annotation.
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
    public @interface NavigationMode {}

    // Declare the constants.
    public static final int NAVIGATION_MODE_STANDARD = 0;
    public static final int NAVIGATION_MODE_LIST = 1;
    public static final int NAVIGATION_MODE_TABS = 2;

    // Decorate the target methods with the annotation.
    @NavigationMode
    public abstract int getNavigationMode();

    // Attach the annotation.
    public abstract void setNavigationMode(@NavigationMode int mode);
}

عند إنشاء هذا الرمز، يتم إنشاء تحذير إذا كانت المَعلمة mode لا تشير إلى أحد الثوابت المحدّدة (NAVIGATION_MODE_STANDARD أو NAVIGATION_MODE_LIST أو NAVIGATION_MODE_TABS).

اجمع @IntDef و@IntRange للإشارة إلى أنّه يمكن أن يكون العدد الصحيح إما مجموعة معيّنة من الثوابت أو قيمة ضمن نطاق.

تفعيل دمج الثوابت مع العلامات

إذا كان بإمكان المستخدمين دمج الثوابت المسموح بها مع علامة (مثل | & و^ وما إلى ذلك)، يمكنك تحديد تعليق توضيحي باستخدام سمة flag للتحقّق مما إذا كانت المَعلمة أو القيمة المعروضة تشير إلى نمط صالح.

ينشئ المثال التالي التعليق التوضيحي DisplayOptions مع قائمة من ثوابت DISPLAY_ الصالحة:

Kotlin

import androidx.annotation.IntDef
...

@IntDef(flag = true, value = [
    DISPLAY_USE_LOGO,
    DISPLAY_SHOW_HOME,
    DISPLAY_HOME_AS_UP,
    DISPLAY_SHOW_TITLE,
    DISPLAY_SHOW_CUSTOM
])
@Retention(AnnotationRetention.SOURCE)
annotation class DisplayOptions
...

Java

import androidx.annotation.IntDef;
...

@IntDef(flag=true, value={
        DISPLAY_USE_LOGO,
        DISPLAY_SHOW_HOME,
        DISPLAY_HOME_AS_UP,
        DISPLAY_SHOW_TITLE,
        DISPLAY_SHOW_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}

...

عند إنشاء رمز باستخدام علامة تعليق توضيحي، يتم إنشاء تحذير إذا كانت المَعلمة المزيّنة أو القيمة المعروضة لا تشير إلى نمط صالح.

الاحتفاظ بالتعليق التوضيحي

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

تنبيه: إنّ الفئات والطرق التي تضيف إليها تعليقات توضيحية باستخدام @Keep تظهر دائمًا في حزمة APK الخاصة بتطبيقك، حتى إذا لم تُشِر أبدًا إلى هذه الفئات والطرق ضمن منطق تطبيقك.

للحفاظ على حجم تطبيقك صغيرًا، ننصحك بالتفكير في ما إذا كان من الضروري الاحتفاظ بكل تعليق توضيحي @Keep في تطبيقك. إذا كنت تستخدِم ميزة "الاستدلال" لمحاولة الوصول إلى فئة أو طريقة مُشارَك فيها تعليق توضيحي، استخدِم شرطًا من نوع -if في قواعد ProGuard، مع تحديد الفئة التي تُجري طلبات الاستدلال.

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

التعليقات التوضيحية لمستوى عرض الرمز

استخدِم التعليقات التوضيحية التالية للإشارة إلى مستوى ظهور أجزاء معيّنة من الرمز البرمجي، مثل methods أو classes أو fields أو packages.

إظهار الرمز للاختبار

يشير التعليق التوضيحي @VisibleForTesting إلى أنّ الطريقة التي تمت إضافة تعليق توضيحي لها أكثر ظهورًا من المعتاد لكي تكون الطريقة قابلة للاختبار. يحتوي هذا التعليق التوضيحي على وسيطة otherwise اختيارية تتيح لك تحديد مستوى إذن الوصول إلى الطريقة في حال عدم الحاجة إلى جعله متاحًا للاختبار. يستخدم Lint الوسيطة otherwise لفرض مستوى العرض المقصود.

في المثال التالي، يكون myMethod() عادةً private، ولكنّه package-private للاختبارات. باستخدام التصنيف VisibleForTesting.PRIVATE ، يعرض lint رسالة إذا تم استدعاء هذه الطريقة من خارج سياق الوصول المسموح به من خلال private، مثل وحدة تجميع مختلفة.

Kotlin

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun myMethod() {
    ...
}

Java

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
void myMethod() { ... }

يمكنك أيضًا تحديد @VisibleForTesting(otherwise = VisibleForTesting.NONE) للإشارة إلى أنّ طريقة الاختبار تتوفّر فقط للاختبار. هذا النموذج هو نفسه المستخدَم في @RestrictTo(TESTS). ويُجري كلاهما عملية التحقّق من الأخطاء النحوية نفسها.

حظر واجهة برمجة تطبيقات

يشير التعليق التوضيحي @RestrictTo إلى أنّ إمكانية الوصول إلى واجهة برمجة التطبيقات التي تتضمّن تعليقات توضيحية (الحزمة أو الفئة أو الطريقة) محدود، على النحو التالي:

الفئات الفرعية

استخدِم نموذج التعليق التوضيحي @RestrictTo(RestrictTo.Scope.SUBCLASSES) لتقييد إمكانية وصول واجهة برمجة التطبيقات إلى الفئات الفرعية فقط.

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

المكتبات

استخدِم نموذج التعليق التوضيحي @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) لحصر الوصول إلى واجهة برمجة التطبيقات على مكتباتك فقط.

لا يمكن سوى لرمز مكتبتك الوصول إلى واجهة برمجة التطبيقات المُشارَك عليها تعليقات توضيحية. يتيح لك ذلك ليس فقط تنظيم رموذك في أي تسلسل هرمي للحزمة تريده، بل يتيح لك أيضًا مشاركة الرمز بين مجموعة من المكتبات ذات الصلة. يتوفّر هذا الخيار حاليًا لمكتبات Jetpack التي تحتوي على الكثير من رموز التنفيذ غير المخصّصة للاستخدام الخارجي، ولكن يجب أن تكون public لمشاركتها في مكتبات Jetpack التكميلية المختلفة.

الاختبار

استخدِم نموذج التعليق التوضيحي @RestrictTo(RestrictTo.Scope.TESTS) لمنع المطوّرين الآخرين من الوصول إلى واجهات برمجة التطبيقات المخصّصة للاختبار.

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