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

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

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

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

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

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

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

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

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

Kotlin

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

رائع

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

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

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

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

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

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

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

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

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

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

ملاحظة: عند إضافة تعليقات توضيحية للقيم الفارغة، قد تقترح ميزة الإكمال التلقائي التعليقات التوضيحية من 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.

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

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

على سبيل المثال، غالبًا ما يعتقد مطوّرو 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) لحصر الوصول إلى واجهة برمجة التطبيقات على الفئات الفرعية فقط.

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

مكتبات

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

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

الاختبار

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

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