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

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

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

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

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

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

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

إضافة تبعية مكتبة Jetpack Annotations

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

Kotlin

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

Groovy

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

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

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

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

إجراء عمليات فحص الرمز البرمجي

لبدء فحص الرمز من "استوديو Android"، والذي يتضمّن التحقّق من صحة التعليقات التوضيحية وإجراء فحص تلقائي باستخدام أداة Lint، انقر على تحليل (Analyze) > فحص الرمز (Inspect Code) من القائمة. يعرض Android Studio رسائل تعارض للإشارة إلى المشاكل المحتملة التي قد تحدث عندما يتعارض الرمز البرمجي مع التعليقات التوضيحية، كما يقترح حلولاً ممكنة.

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

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

التعليقات التوضيحية الخاصة بقيمة Null

يمكن أن تكون تعليقات توضيحية بشأن القيم الفارغة مفيدة في رمز 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 Studio التعليقَين التوضيحيَّين @Nullable و@NonNull في Android في المواقع التي تم رصدها في الرمز البرمجي. بعد إجراء تحليل القيم الفارغة، من الممارسات الجيدة التحقّق من التعليقات التوضيحية التي تم إدخالها.

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

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

يمكن أن يكون التحقّق من صحة أنواع الموارد مفيدًا لأنّ مراجع Android للموارد، مثل موارد drawable وstring، يتم تمريرها كأعداد صحيحة.

يمكن تمرير الرمز الذي يتوقّع أن تشير مَعلمة إلى نوع معيّن من الموارد، مثل 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 إلى الطرق المرتبطة بدورة حياة التطبيق فقط.

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

من الاستخدامات الشائعة لتعليقات توضيحية خاصة بسلسلة المحادثات التحقّق من أنّ الطرق أو الفئات التي تمّت إضافة التعليق التوضيحي @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 مما إذا كانت قيمة مَعلمة من النوع float أو double ضمن نطاق محدّد من قيم النقطة العائمة. يوضّح المثال التالي أنّ المَعلمة 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) {
    //...
}

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

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:

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 إلى كل طريقة غير فارغة، أضِف التعليق التوضيحي لتوضيح نتائج الطرق التي قد تكون مربكة.

على سبيل المثال، يعتقد مطوّرو 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 annotations

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

يوضّح المثال التالي كيفية إضافة تعليق توضيحي إلى الطريقة onCreate() لضمان أنّ أي عمليات تنفيذ للطريقة تتجاوز الطريقة الأصلية تستدعي super.onCreate():

Kotlin

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

Java

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

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

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

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

تستخدم تعليقات Typedef التوضيحية @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، مع تحديد الفئة التي تجري عمليات استدعاء الانعكاس.

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

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

استخدِم التعليقات التوضيحية التالية للإشارة إلى مستوى رؤية أجزاء معيّنة من الرمز، مثل الطرق أو الفئات أو الحقول أو الحِزم.

إتاحة الرمز البرمجي للاختبار

تشير التعليق التوضيحي @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) لحصر الوصول إلى واجهة برمجة التطبيقات على الفئات الفرعية فقط.

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

المكتبات

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

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

الاختبار

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

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