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

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

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

يتيح Android مجموعة متنوعة من التعليقات التوضيحية من خلال مكتبة التعليقات التوضيحية في Jetpack. يمكنك الوصول إلى المكتبة من خلال حزمة 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.11.0")
}

أنيق

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

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

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

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

تشغيل عمليات فحص الرموز البرمجية

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

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

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

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

يمكن أن تكون تعليقات Nullness التوضيحية مفيدة في رمز 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" إجراء تحليل لإمكانية قبول القيم الفارغة من أجل استنتاج تعليقات توضيحية حول القيمة الفارغة وإدراجها تلقائيًا في الرمز البرمجي. يفحص تحليل إمكانية قبول القيم الفارغة العقود في جميع التسلسلات الهرمية للطرق في الرمز البرمجي لرصد ما يلي:

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

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

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

ملاحظة: عند إضافة تعليقات توضيحية حول القيم الفارغة، قد يقترح الإكمال التلقائي التعليقات التوضيحية @Nullable و@NotNull في IntelliJ بدلاً من التعليقات التوضيحية حول القيم الفارغة في Android، وقد يستورد المكتبة المقابلة تلقائيًا. ومع ذلك، فإنّ أداة التحقّق من الأخطاء في "استوديو Android" تبحث فقط عن تعليقات 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، بدلاً من عدد صحيح للون، إلى الطرق التي تتضمّن تعليقات توضيحية.

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

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

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

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

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

المكتبات

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

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

الاختبار

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

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