بازرسی کد را با حاشیه نویسی بهبود بخشید

استفاده از ابزارهای بررسی کد، مانند lint ، می‌تواند به شما در یافتن مشکلات و بهبود کدتان کمک کند، اما ابزارهای بررسی فقط می‌توانند تا حدی استنباط کنند. برای مثال، شناسه‌های منابع اندروید از یک int برای شناسایی رشته‌ها، گرافیک‌ها، رنگ‌ها و سایر انواع منابع استفاده می‌کنند، بنابراین ابزارهای بررسی نمی‌توانند تشخیص دهند که چه زمانی یک منبع رشته‌ای را مشخص کرده‌اید که باید یک رنگ را مشخص می‌کردید. این وضعیت به این معنی است که برنامه شما ممکن است به طور نادرست رندر شود یا اصلاً اجرا نشود، حتی اگر از بررسی کد استفاده کنید.

حاشیه‌نویسی‌ها به شما این امکان را می‌دهند که به ابزارهای بازرسی کد، مانند lint، نکاتی را ارائه دهید تا به تشخیص این مشکلات ظریف‌تر کد کمک کنند. حاشیه‌نویسی‌ها به عنوان برچسب‌های فراداده‌ای اضافه می‌شوند که شما به متغیرها، پارامترها و مقادیر برگشتی متصل می‌کنید تا مقادیر برگشتی متد، پارامترهای ارسالی، متغیرهای محلی و فیلدها را بررسی کنید. هنگامی که از حاشیه‌نویسی‌ها با ابزارهای بازرسی کد استفاده می‌شود، می‌تواند به شما در تشخیص مشکلاتی مانند استثنائات اشاره‌گر تهی و تداخل نوع منبع کمک کند.

اندروید از طریق کتابخانه‌ی Jetpack Annotations از انواع مختلفی از حاشیه‌نویسی‌ها پشتیبانی می‌کند. شما می‌توانید از طریق پکیج androidx.annotation به این کتابخانه دسترسی داشته باشید.

نکته: اگر یک ماژول به یک پردازنده‌ی حاشیه‌نویسی (annotation processor) وابستگی دارد، برای اضافه کردن آن وابستگی باید از پیکربندی وابستگی kapt یا ksp برای کاتلین یا پیکربندی وابستگی annotationProcessor برای جاوا استفاده کنید.

اضافه کردن حاشیه‌نویسی به پروژه

برای فعال کردن حاشیه‌نویسی‌ها در پروژه خود، وابستگی androidx.annotation:annotation به کتابخانه یا برنامه خود اضافه کنید. هر حاشیه‌نویسی که اضافه می‌کنید، هنگام اجرای یک بازرسی کد یا وظیفه lint بررسی می‌شود.

وابستگی کتابخانه Jetpack Annotations را اضافه کنید

کتابخانه Jetpack Annotations در مخزن Maven گوگل منتشر شده است. برای افزودن کتابخانه Jetpack Anotations به پروژه خود، خط زیر را در بلوک dependencies فایل build.gradle یا build.gradle.kts خود وارد کنید:

کاتلین

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

گرووی

dependencies {
    implementation 'androidx.annotation:annotation:1.9.1'
}
سپس، در نوار ابزار یا اعلان همگام‌سازی که ظاهر می‌شود، روی «همگام‌سازی اکنون» کلیک کنید.

اگر از حاشیه‌نویسی‌ها در ماژول کتابخانه خود استفاده می‌کنید، این حاشیه‌نویسی‌ها به عنوان بخشی از آرشیو اندروید (AAR) در قالب XML در فایل annotations.zip گنجانده می‌شوند. افزودن وابستگی androidx.annotation هیچ وابستگی برای هیچ یک از کاربران پایین‌دستی کتابخانه شما ایجاد نمی‌کند.

نکته: اگر از کتابخانه‌های دیگر Jetpack استفاده می‌کنید، ممکن است نیازی به اضافه کردن وابستگی androidx.annotation نداشته باشید. از آنجا که بسیاری از کتابخانه‌های دیگر Jetpack به کتابخانه Annotations وابسته هستند، ممکن است از قبل به annotations دسترسی داشته باشید.

برای مشاهده‌ی لیست کاملی از حاشیه‌نویسی‌های موجود در مخزن Jetpack، می‌توانید به مرجع کتابخانه‌ی Jetpack Annotations مراجعه کنید یا از ویژگی تکمیل خودکار برای نمایش گزینه‌های موجود برای دستور import androidx.annotation. استفاده کنید.

اجرای بازرسی‌های کد

برای شروع بازرسی کد از اندروید استودیو، که شامل اعتبارسنجی حاشیه‌نویسی‌ها و بررسی خودکار پرزها می‌شود، از منو گزینه Analyze > Inspect Code را انتخاب کنید. اندروید استودیو پیام‌های مغایرت را نمایش می‌دهد تا مشکلات احتمالی را که کد شما با حاشیه‌نویسی‌ها مغایرت دارد، علامت‌گذاری کند و راه‌حل‌های ممکن را پیشنهاد دهد.

همچنین می‌توانید با اجرای وظیفه lint با استفاده از خط فرمان، حاشیه‌نویسی‌ها را اعمال کنید. اگرچه این ممکن است برای علامت‌گذاری مشکلات با یک سرور یکپارچه‌سازی مداوم مفید باشد، وظیفه lint حاشیه‌نویسی‌های nullness را اعمال نمی‌کند (که در بخش بعدی توضیح داده شده است)؛ فقط اندروید استودیو این کار را انجام می‌دهد. برای اطلاعات بیشتر در مورد فعال‌سازی و اجرای بازرسی‌های lint، به بهبود کد خود با بررسی‌های lint مراجعه کنید.

اگرچه تداخل حاشیه‌نویسی باعث ایجاد هشدار می‌شود، اما این هشدارها مانع از کامپایل برنامه شما نمی‌شوند.

حاشیه‌نویسی‌های تهی

حاشیه‌نویسی‌های Nullness می‌توانند در کد جاوا برای اعمال کنترل بر null بودن مقادیر مفید باشند. آن‌ها در کد کاتلین کاربرد کمتری دارند، زیرا کاتلین قوانین nullability را به صورت داخلی دارد که در زمان کامپایل اعمال می‌شوند.

برای بررسی تهی بودن یک متغیر، پارامتر یا مقدار بازگشتی، می‌توان از حاشیه‌نویسی‌های @Nullable و @NonNull استفاده کرد. حاشیه‌نویسی @Nullable نشان‌دهنده‌ی متغیر، پارامتر یا مقدار بازگشتی است که می‌تواند تهی باشد. @NonNull نشان‌دهنده‌ی متغیر، پارامتر یا مقدار بازگشتی است که نمی‌تواند تهی باشد.

برای مثال، اگر یک متغیر محلی که حاوی مقدار null است به عنوان پارامتر به متدی با حاشیه‌نویسی @NonNull متصل به آن پارامتر ارسال شود، ساخت کد یک هشدار مبنی بر تداخل غیر null ایجاد می‌کند. همچنین، تلاش برای ارجاع به نتیجه متدی که با @Nullable مشخص شده است بدون بررسی اولیه اینکه آیا نتیجه null است یا خیر، یک هشدار nullness ایجاد می‌کند. فقط در صورتی از @Nullable روی مقدار بازگشتی یک متد استفاده کنید که هر بار استفاده از متد باید به صراحت null-checked باشد.

مثال زیر قابلیت تهی‌پذیری را در عمل نشان می‌دهد. کد مثال کاتلین از حاشیه‌نویسی @NonNull استفاده نمی‌کند زیرا وقتی یک نوع غیرتهی‌پذیر مشخص می‌شود، به طور خودکار به بایت‌کد تولید شده اضافه می‌شود. مثال جاوا از حاشیه‌نویسی @NonNull روی پارامترهای context و attrs استفاده می‌کند تا بررسی کند که مقادیر پارامتر ارسالی تهی نباشند. همچنین بررسی می‌کند که خود متد onCreateView() مقدار تهی برنمی‌گرداند:

کاتلین

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

جاوا

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

تحلیل بطلان‌پذیری

اندروید استودیو از اجرای یک تحلیل nullability برای استنباط و درج خودکار حاشیه‌نویسی‌های nullness در کد شما پشتیبانی می‌کند. یک تحلیل nullability، قراردادها را در سراسر سلسله مراتب متد در کد شما اسکن می‌کند تا موارد زیر را تشخیص دهد:

  • فراخوانی متدهایی که می‌توانند مقدار null را برگردانند.
  • متدهایی که نباید مقدار null برگردانند.
  • متغیرهایی مانند فیلدها، متغیرهای محلی و پارامترها که می‌توانند تهی (null) باشند.
  • متغیرهایی مانند فیلدها، متغیرهای محلی و پارامترها که نمی‌توانند مقدار تهی (null) را در خود نگه دارند.

سپس تجزیه و تحلیل به طور خودکار حاشیه‌نویسی‌های تهی مناسب را در مکان‌های شناسایی شده درج می‌کند.

برای اجرای تحلیل نال‌پذیری در اندروید استودیو، Analyze > Infer Nullity را انتخاب کنید. اندروید استودیو حاشیه‌نویسی‌های Android @Nullable و @NonNull را در مکان‌های شناسایی‌شده در کد شما وارد می‌کند. پس از اجرای تحلیل نال، بهتر است حاشیه‌نویسی‌های تزریق‌شده را تأیید کنید.

نکته: هنگام افزودن حاشیه‌نویسی‌های nullness، تکمیل خودکار ممکن است حاشیه‌نویسی‌های IntelliJ @Nullable و @NotNull را به جای حاشیه‌نویسی‌های null اندروید پیشنهاد دهد و ممکن است کتابخانه مربوطه را به صورت خودکار وارد کند. با این حال، بررسی‌کننده‌ی lint اندروید استودیو فقط به دنبال حاشیه‌نویسی‌های null اندروید می‌گردد. هنگام تأیید حاشیه‌نویسی‌های خود، تأیید کنید که پروژه شما از حاشیه‌نویسی‌های null اندروید استفاده می‌کند تا بررسی‌کننده‌ی lint بتواند در حین بررسی کد به درستی به شما اطلاع دهد.

حاشیه‌نویسی منابع

اعتبارسنجی انواع منابع می‌تواند مفید باشد زیرا ارجاعات اندروید به منابع، مانند منابع drawable و رشته‌ای ، به صورت اعداد صحیح ارسال می‌شوند.

کدی که انتظار دارد یک پارامتر به نوع خاصی از منبع، مانند یک String ، اشاره کند، می‌تواند به نوع مرجع مورد انتظار int ارسال شود، اما در واقع به نوع متفاوتی از منبع، مانند یک منبع R.string اشاره کند.

برای مثال، حاشیه‌نویسی‌های @StringRes را اضافه کنید تا بررسی کنید که آیا یک پارامتر منبع شامل یک مرجع R.string است یا خیر، همانطور که در اینجا نشان داده شده است:

کاتلین

abstract fun setTitle(@StringRes resId: Int)

جاوا

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 حاشیه‌نویسی کنید.

اگر همه متدهای یک کلاس الزام threading یکسانی داشته باشند، می‌توانید یک حاشیه‌نویسی thread به کلاس اضافه کنید تا تأیید کنید که همه متدهای کلاس از یک نوع thread فراخوانی می‌شوند.

یکی از کاربردهای رایج حاشیه‌نویسی‌های نخ، اعتبارسنجی این است که متدها یا کلاس‌های حاشیه‌نویسی‌شده با @WorkerThread فقط از یک نخ پس‌زمینه‌ی مناسب فراخوانی شوند.

حاشیه‌نویسی‌های محدودیت ارزش

از حاشیه‌نویسی‌های @IntRange ، @FloatRange و @Size برای اعتبارسنجی مقادیر پارامترهای ارسالی استفاده کنید. هر دو @IntRange و @FloatRange زمانی بیشترین کاربرد را دارند که روی پارامترهایی اعمال شوند که احتمال دارد کاربران محدوده را اشتباه وارد کنند.

حاشیه‌نویسی @IntRange اعتبارسنجی می‌کند که مقدار یک پارامتر صحیح یا طولانی در محدوده‌ی مشخص‌شده قرار دارد. مثال زیر نشان می‌دهد که پارامتر alpha باید حاوی یک مقدار صحیح از ۰ تا ۲۵۵ باشد:

کاتلین

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

جاوا

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

حاشیه‌نویسی @FloatRange بررسی می‌کند که آیا مقدار پارامتر float یا double در محدوده‌ی مشخصی از مقادیر ممیز شناور قرار دارد یا خیر. مثال زیر نشان می‌دهد که پارامتر alpha باید حاوی مقدار float از 0.0 تا 1.0 باشد:

کاتلین

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

جاوا

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 باید حداقل شامل یک عنصر باشد:

کاتلین

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

جاوا

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

حاشیه‌نویسی‌های مجوز

از حاشیه‌نویسی @RequiresPermission برای اعتبارسنجی مجوزهای فراخوانی‌کننده‌ی یک متد استفاده کنید. برای بررسی وجود یک مجوز واحد از فهرست مجوزهای معتبر، از ویژگی anyOf استفاده کنید. برای بررسی مجموعه‌ای از مجوزها، از ویژگی allOf استفاده کنید. مثال زیر، متد setWallpaper() را حاشیه‌نویسی می‌کند تا نشان دهد که فراخوانی‌کننده‌ی متد باید مجوز permission.SET_WALLPAPERS را داشته باشد:

کاتلین

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

جاوا

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

مثال زیر مستلزم آن است که فراخوانی‌کننده‌ی متد copyImageFile() هم دسترسی خواندن به حافظه‌ی خارجی و هم دسترسی خواندن به متادیتای مکان در تصویر کپی‌شده را داشته باشد:

کاتلین

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

جاوا

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

برای مجوزهای مربوط به intentها، الزام مجوز را در فیلد رشته‌ای که نام اکشن intent را تعریف می‌کند، قرار دهید:

کاتلین

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

جاوا

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

برای مجوزهای مربوط به ارائه‌دهندگان محتوا که به مجوزهای جداگانه برای دسترسی خواندن و نوشتن نیاز دارند، هر الزام مجوز را در حاشیه‌نویسی @RequiresPermission.Read یا @RequiresPermission.Write قرار دهید:

کاتلین

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

جاوا

@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) از یک مجوز غیرمستقیم برای intent ارسال شده به متد استفاده می‌کند:

کاتلین

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

جاوا

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

وقتی از مجوزهای غیرمستقیم استفاده می‌کنید، ابزارهای ساخت، تحلیل جریان داده را انجام می‌دهند تا بررسی کنند که آیا آرگومان ارسالی به متد، حاشیه‌نویسی‌های @RequiresPermission دارد یا خیر. سپس، هرگونه حاشیه‌نویسی موجود از پارامتر را روی خود متد اعمال می‌کنند. در مثال startActivity(Intent) ، حاشیه‌نویسی‌های کلاس Intent باعث ایجاد هشدارهایی در مورد استفاده‌های نامعتبر از startActivity(Intent) می‌شوند، زمانی که یک intent بدون مجوزهای مناسب به متد ارسال می‌شود، همانطور که در شکل ۱ نشان داده شده است.

شکل ۱. هشدار تولید شده از حاشیه‌نویسی مجوزهای غیرمستقیم در متد startActivity(Intent) .

ابزارهای ساخت، هشدار را در startActivity(Intent) از حاشیه‌نویسی روی نام اکشن intent مربوطه در کلاس Intent تولید می‌کنند:

کاتلین

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

جاوا

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

در صورت لزوم، می‌توانید هنگام حاشیه‌نویسی پارامتر یک متد @RequiresPermission با @RequiresPermission.Read یا @RequiresPermission.Write جایگزین کنید. با این حال، برای مجوزهای غیرمستقیم، @RequiresPermission نباید همراه با حاشیه‌نویسی‌های مجوزهای خواندن یا نوشتن استفاده شود.

حاشیه‌نویسی مقادیر بازگشتی

از حاشیه‌نویسی @CheckResult برای تأیید صحت استفاده از نتیجه یا مقدار بازگشتی یک متد استفاده کنید. به جای حاشیه‌نویسی هر متد غیر void با @CheckResult ، این حاشیه‌نویسی را اضافه کنید تا نتایج متدهای بالقوه گیج‌کننده را روشن کنید.

برای مثال، توسعه‌دهندگان تازه‌کار جاوا اغلب به اشتباه فکر می‌کنند که < String >.trim() فضای خالی را از رشته اصلی حذف می‌کند. حاشیه‌نویسی متد با پرچم‌های @CheckResult از < String >.trim() استفاده می‌کند، در حالی که فراخواننده هیچ کاری با مقدار بازگشتی متد انجام نمی‌دهد.

مثال زیر متد checkPermissions() را حاشیه‌نویسی می‌کند تا بررسی کند که آیا مقدار برگشتی متد واقعاً مورد ارجاع قرار گرفته است یا خیر. همچنین متد enforcePermission() را به عنوان متدی که به عنوان جایگزین به توسعه‌دهنده پیشنهاد می‌شود، نامگذاری می‌کند:

کاتلین

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

جاوا

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

حاشیه‌نویسی‌های CallSuper

از حاشیه‌نویسی @CallSuper برای تأیید اینکه یک متد override، پیاده‌سازی super متد را فراخوانی می‌کند، استفاده کنید.

مثال زیر متد onCreate() را حاشیه‌نویسی می‌کند تا اطمینان حاصل شود که هرگونه پیاده‌سازی متد override، super.onCreate() را فراخوانی می‌کند:

کاتلین

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

جاوا

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

حاشیه‌نویسی‌های Typedef

حاشیه‌نویسی‌های Typedef بررسی می‌کنند که آیا یک پارامتر خاص، مقدار بازگشتی یا فیلد به مجموعه‌ای خاص از ثابت‌ها اشاره دارد یا خیر. آن‌ها همچنین امکان تکمیل کد را فراهم می‌کنند تا ثابت‌های مجاز به طور خودکار ارائه شوند.

از حاشیه‌نویسی‌های @IntDef و @StringDef برای ایجاد حاشیه‌نویسی‌های شمارشی از مجموعه‌های عدد صحیح و رشته‌ای استفاده کنید تا بتوانید انواع دیگر ارجاعات کد را اعتبارسنجی کنید.

حاشیه‌نویسی‌های Typedef از @interface برای اعلام نوع جدید حاشیه‌نویسی شمارشی استفاده می‌کنند. حاشیه‌نویسی‌های @IntDef و @StringDef ، به همراه @Retention ، حاشیه‌نویسی جدید را حاشیه‌نویسی می‌کنند و برای تعریف نوع شمارشی ضروری هستند. حاشیه‌نویسی @Retention(RetentionPolicy.SOURCE) به کامپایلر می‌گوید که داده‌های حاشیه‌نویسی شمارشی را در فایل .class ذخیره نکند.

مثال زیر مراحل ایجاد یک حاشیه‌نویسی را نشان می‌دهد که بررسی می‌کند آیا مقداری که به عنوان پارامتر متد ارسال می‌شود به یکی از ثابت‌های تعریف شده اشاره دارد یا خیر:

کاتلین

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

}

جاوا

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_ ایجاد می‌کند:

کاتلین

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
...

جاوا

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 تضمین می‌کند که یک کلاس یا متد حاشیه‌نویسی شده، هنگام کوچک‌سازی کد در زمان ساخت، حذف نشود. این حاشیه‌نویسی معمولاً به متدها و کلاس‌هایی که از طریق reflection قابل دسترسی هستند اضافه می‌شود تا از برخورد کامپایلر با کد به عنوان کد استفاده نشده جلوگیری شود.

احتیاط: کلاس‌ها و متدهایی که با استفاده از @Keep حاشیه‌نویسی می‌کنید، همیشه در APK برنامه شما ظاهر می‌شوند، حتی اگر هرگز در منطق برنامه خود به این کلاس‌ها و متدها ارجاع ندهید.

برای کوچک نگه داشتن حجم برنامه، در نظر بگیرید که آیا حفظ هر حاشیه‌نویسی @Keep در برنامه ضروری است یا خیر. اگر از reflection برای دسترسی به یک کلاس یا متد حاشیه‌نویسی شده استفاده می‌کنید، از شرط -if در قوانین ProGuard خود استفاده کنید و کلاسی را که فراخوانی‌های reflection را انجام می‌دهد، مشخص کنید.

برای اطلاعات بیشتر در مورد نحوه کوچک‌سازی کد و مشخص کردن اینکه کدام کدها نباید حذف شوند، به بخش کوچک‌سازی، مبهم‌سازی و بهینه‌سازی برنامه خود مراجعه کنید.

حاشیه‌نویسی‌های مربوط به قابلیت مشاهده کد

از حاشیه‌نویسی‌های زیر برای مشخص کردن میزان دیده شدن بخش‌های خاصی از کد، مانند متدها، کلاس‌ها، فیلدها یا بسته‌ها استفاده کنید.

کد را برای آزمایش قابل مشاهده کنید

حاشیه‌نویسی @VisibleForTesting نشان می‌دهد که یک متد حاشیه‌نویسی‌شده، بیش از حد معمول برای قابل آزمایش کردن متد، قابل مشاهده است. این حاشیه‌نویسی یک آرگومان اختیاری otherwise دارد که به شما امکان می‌دهد میزان قابل مشاهده بودن متد را در صورت عدم نیاز به قابل مشاهده بودن برای آزمایش، تعیین کنید. Lint از آرگومان otherwise برای اعمال میزان قابل مشاهده بودن مورد نظر استفاده می‌کند.

در مثال زیر، myMethod() معمولاً private است، اما برای تست‌ها به package-private در می‌آید. با استفاده از VisibleForTesting.PRIVATE ، اگر این متد از خارج از چارچوب مجاز توسط دسترسی private فراخوانی شود، مثلاً از یک واحد کامپایل متفاوت، lint پیامی را نمایش می‌دهد.

کاتلین

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

جاوا

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

همچنین می‌توانید @VisibleForTesting(otherwise = VisibleForTesting.NONE) را برای نشان دادن اینکه یک متد فقط برای تست وجود دارد، مشخص کنید. این حالت مانند استفاده از @RestrictTo(TESTS) است. هر دو بررسی lint یکسانی را انجام می‌دهند.

محدود کردن یک API

حاشیه‌نویسی @RestrictTo نشان می‌دهد که دسترسی به API حاشیه‌نویسی‌شده (بسته، کلاس یا متد) به شرح زیر محدود شده است:

زیرکلاس‌ها

از فرم حاشیه‌نویسی @RestrictTo(RestrictTo.Scope.SUBCLASSES) برای محدود کردن دسترسی API فقط به زیرکلاس‌ها استفاده کنید.

فقط کلاس‌هایی که کلاس حاشیه‌نویسی‌شده را ارث‌بری می‌کنند می‌توانند به این API دسترسی داشته باشند. اصلاح‌کننده‌ی protected جاوا به اندازه‌ی کافی محدودکننده نیست، زیرا امکان دسترسی از کلاس‌های غیرمرتبط درون همان بسته را فراهم می‌کند. همچنین، مواردی وجود دارد که می‌خواهید یک متد را برای انعطاف‌پذیری بیشتر در آینده public کنید، زیرا هرگز نمی‌توانید یک متد قبلاً protected و بازنویسی‌شده را public کنید، اما می‌خواهید اشاره‌ای ارائه دهید که این کلاس برای استفاده در داخل کلاس یا فقط از زیرکلاس‌ها در نظر گرفته شده است.

کتابخانه‌ها

از فرم حاشیه‌نویسی @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) برای محدود کردن دسترسی API فقط به کتابخانه‌های خود استفاده کنید.

فقط کد کتابخانه شما می‌تواند به API حاشیه‌نویسی‌شده دسترسی داشته باشد. این به شما امکان می‌دهد نه تنها کد خود را در هر سلسله مراتب بسته‌ای که می‌خواهید سازماندهی کنید، بلکه کد را بین گروهی از کتابخانه‌های مرتبط نیز به اشتراک بگذارید. این گزینه در حال حاضر برای کتابخانه‌های Jetpack که کد پیاده‌سازی زیادی دارند که برای استفاده خارجی در نظر گرفته نشده است، اما باید public باشد تا بتوان آن را در بین کتابخانه‌های مختلف مکمل Jetpack به اشتراک گذاشت، در دسترس است.

آزمایش

برای جلوگیری از دسترسی سایر توسعه‌دهندگان به APIهای تست خود، از فرم حاشیه‌نویسی @RestrictTo(RestrictTo.Scope.TESTS) استفاده کنید.

فقط کد آزمایشی می‌تواند به API حاشیه‌نویسی‌شده دسترسی داشته باشد. این کار مانع از آن می‌شود که سایر توسعه‌دهندگان از APIهایی برای توسعه استفاده کنند که شما فقط برای اهداف آزمایشی در نظر گرفته‌اید.