حاشیه‌نویسی‌ها در AIDL

‫AIDL از حاشیه‌نویسی‌هایی پشتیبانی می‌کند که اطلاعات بیشتری درباره عنصر حاشیه‌نویسی‌شده به کامپایلر AIDL می‌دهند، که این امر بر کد تهی‌سازه تولیدشده نیز تأثیر می‌گذارد.

نحو آن مشابه نحو Java است:

@AnnotationName(argument1=value, argument2=value) AidlEntity

در اینجا، AnnotationName نام گزارمان است و AidlEntity یک نهاد AIDL مانند interface Foo، void method()، یا int arg است. حاشیه‌نویسی به نهادی که پس‌از آن می‌آید پیوست می‌شود.

برخی‌از گزارمان‌ها می‌توانند آرگومان‌هایی داشته باشند که درون پرانتز تنظیم شده‌اند، همان‌طور که در مثال قبلی نشان داده شده است. گزارمان‌هایی که آرگومان ندارند نیازی به پرانتز ندارند. برای مثال:

@AnnotationName AidlEntity

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

در اینجا فهرست نشان‌گذاری‌های ازپیش‌تعریف‌شده AIDL آمده است:

حاشیه‌نویسی‌هادر نسخه Android اضافه شده است
nullable7
utf8InCpp7
VintfStability11
UnsupportedAppUsage10
Hide11
Backing11
NdkOnlyStableParcelable14
JavaOnlyStableParcelable11
JavaDerive12
JavaPassthrough12
FixedSize12
Descriptor12

تهی‌پذیر

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

این حاشیه‌نویسی را فقط می‌توانید به انواع پاسخ روش، پارامترهای روش، و فیلدهای قابل‌بسته‌بندی پیوست کنید:

interface IFoo {
    // method return types
    @nullable Data method();

    // method parameters
    void method2(in @nullable Data d);
}

parcelable Data {
    // parcelable fields
    @nullable Data d;
}

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

void method(in @nullable int a); // int is a primitive type

این گزارمان برای زیرینه Java بدون عملیات است. در جاوا، همه انواع غیرابتدایی با ارجاع ارسال می‌شوند، که می‌تواند null باشد.

در زیرینه CPP،‏ @nullable T در Android 11 یا پایین‌تر به std::unique_ptr<T> و در Android 12 یا بالاتر به std::optional<T> نگاشت می‌شود.

در زیرینه NDK، @nullable T به std::optional<T> نگاشت می‌شود.

در زیرینه Rust، @nullable T به Option<T> نگاشت می‌شود.

برای نوع فهرست‌مانند L مثل T[] یا List<T>، @nullable L به std::optional<std::vector<std::optional<T>>> (یا std::unique_ptr<std::vector<std::unique_ptr<T>>> درصورت استفاده از زیرینه CPP برای Android 11 یا پایین‌تر) نگاشت می‌شود.

این نقشه‌برداری استثنایی دارد. وقتی T، IBinder یا یک میانای AIDL باشد، @nullable برای پس‌زمینه‌های Java،‏ CPP، و NDK یک عملیات بی‌تأثیر است. برای مثال، در زیرینه CPP، هم @nullable IBinder و هم IBinder به‌طور یکسان به android::sp<IBinder> نگاشت می‌شوند، که ازقبل تهی‌پذیر است زیرا اشاره‌گر قوی است (CPP و NDK همچنان تهی‌پذیر بودن را اعمال می‌کنند، اما نوع android::sp<IBinder> یا ndk::SpAIBinder باقی می‌ماند).

در زیرینه Rust، انواع که Default (IBinder، میاناهای AIDL،‏ ParcelFileDescriptor، و بسته‌های بدون ساختار) را پیاده‌سازی نمی‌کنند، بسته به زمینه‌شان به Option<T> نگاشت می‌شوند:

  • انواع برگشتی روش، in پارامتر، و inout پارامتر: این انواع فقط درصورتی تهی‌پذیر هستند که با @nullable حاشیه‌نویسی شده باشند و به Option<T> (یا Option<&T> برای in پارامتر و &mut Option<T> برای inout پارامتر) نگاشت شوند. بدون @nullable، مستقیماً به T (یا &T و &mut T) نگاشت می‌شوند.
  • فیلدهای Parcelable و پارامترهای out: ازآنجایی‌که parcelableهای Rust تولیدشده از Default مشتق می‌شوند و پارامترهای out با مقدار پیش‌فرض مقداردهی اولیه می‌شوند، این نوع‌ها همیشه به Option<T> نگاشت می‌شوند (و آرایه‌های اندازه ثابت در این زمینه‌ها به [Option<T>; N] نگاشت می‌شوند، درحالی‌که آرایه‌های پویا در پارامترهای out به Vec<Option<T>> نگاشت می‌شوند)، حتی بدون @nullable. وقتی با @nullable حاشیه‌نویسی نشده باشد، غیرقابل‌تهی بودن همچنان در زمان اجرا درطول بسته‌بندی اعمال می‌شود (اگر مقدار None باشد، StatusCode::UNEXPECTED_NULL برگردانده می‌شود).

از Android 13 به بعد، از @nullable(heap=true) می‌توان برای فیلدهای بسته‌شدنی برای مدل‌سازی انواع بازگشتی استفاده کرد. نمی‌توان از @nullable(heap=true) با پارامترهای روش یا انواع برگشتی استفاده کرد. وقتی با آن حاشیه‌نویسی می‌شود، فیلد به مرجع اختصاص‌یافته به توده std::unique_ptr<T> در پس‌زمینه‌های CPP و NDK نگاشت می‌شود. @nullable(heap=true) در زیرینه Java بدون عملیات است.

utf8InCpp

‫utf8InCpp اعلام می‌کند که String در قالب UTF8 برای کارساز CPP نشان داده می‌شود. همان‌طور که از نام آن پیداست، این گزارمان برای دیگر زیرینه‌ها یک عملیات بدون اثر است. به‌طور دقیق، String همیشه در پسای Java به‌صورت UTF16 و در پسای NDK به‌صورت UTF8 است.

این گزارمان می‌تواند در هر جایی که نوع String می‌تواند استفاده شود پیوست شود، ازجمله مقادیر برگشتی، پارامترها، بیانیه‌های ثابت، و فیلدهای بسته‌شدنی.

برای زیرینه CPP،‏ @utf8InCpp String در AIDL به std::string نگاشت می‌شود، درحالی‌که String بدون گزارمان به android::String16 نگاشت می‌شود که در آن از UTF16 استفاده می‌شود.

VintfStability

VintfStability اعلام می‌کند که نوع تعریف‌شده توسط کاربر (میانای، parcelable، و enum) می‌تواند در سراسر دامنه‌های سیستم و فروشنده استفاده شود. برای اطلاعات بیشتر درباره قابلیت هم‌کنش‌پذیری سیستم-فروشنده، AIDL برای HAL را ببینید.

حاشیه‌نویسی امضای نوع را تغییر نمی‌دهد، اما وقتی تنظیم می‌شود، نمونه نوع به‌عنوان پایدار علامت‌گذاری می‌شود تا بتواند در سراسر فروشنده و فرایندهای سیستم حرکت کند.

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

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

وقتی نوعی با VintfStability حاشیه‌نویسی می‌شود، هر نوع دیگری که در آن نوع به آن ارجاع داده می‌شود نیز باید به همین صورت حاشیه‌نویسی شود. در مثال زیر، Data و IBar باید هر دو با VintfStability حاشیه‌نویسی شوند:

@VintfStability
interface IFoo {
    void doSomething(in IBar b); // references IBar
    void doAnother(in Data d); // references Data
}

@VintfStability // required
interface IBar {...}

@VintfStability // required
parcelable Data {...}

علاوه‌براین، فایل‌های AIDL که انواع حاشیه‌نویسی‌شده با VintfStability را تعریف می‌کنند فقط بااستفاده از نوع ماژول aidl_interface Soong قابل‌ساخت هستند، درحالی‌که ویژگی stability روی vintf تنظیم شده است:

aidl_interface {
    name: "my_interface",
    srcs: [...],
    stability: "vintf",
}

UnsupportedAppUsage

گزارمان UnsupportedAppUsage نشان می‌دهد که نوع AIDL گزارمان‌گذاری‌شده بخشی از میانای غیر SDK است که برای برنامه‌های قدیمی دردسترس بوده است. برای کسب اطلاعات بیشتر درباره میاناهای برنامه‌سازی کاربردی پنهان، محدودیت‌های مربوط به میاناهای غیر SDK را ببینید.

حاشیه‌نویسی UnsupportedAppUsage بر رفتار کد تولیدشده تأثیری ندارد. حاشیه‌نویسی فقط کلاس Java تولیدشده را با حاشیه‌نویسی Java با همان نام حاشیه‌نویسی می‌کند:

// in AIDL
@UnsupportedAppUsage
interface IFoo {...}

// in Java
@android.compat.annotation.UnsupportedAppUsage
public interface IFoo {...}

این یک عملیات بدون اثر برای زمینه‌های غیرجاوا است.

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

حاشیه‌نویسی Backing نوع ذخیره‌سازی نوع شمارشی AIDL را مشخص می‌کند:

@Backing(type="int")
enum Color { RED, BLUE, }

در پسای CPP، این یک کلاس شمارشی C++‎ از نوع int32_t منتشر می‌کند:

enum class Color : int32_t {
    RED = 0,
    BLUE = 1,
}

اگر حاشیه‌نویسی حذف شود، فرض می‌شود type byte است که به int8_t برای باطن CPP نگاشت می‌شود.

آرگومان type را فقط می‌توان برای انواع انتگرال زیر تنظیم کرد:

  • ‫byte (۸ بیت پهنا)
  • ‫int (عرض ۳۲ بیت)
  • ‫long (عرض ۶۴ بیت)

NdkOnlyStableParcelable

NdkOnlyStableParcelable اعلامیه بسته‌شدنی (نه تعریف) را به‌عنوان پایدار علامت‌گذاری می‌کند تا بتواند از انواع AIDL پایدار دیگر ارجاع داده شود. این شبیه JavaOnlyStableParcelable است، اما NdkOnlyStableParcelable اعلامیه دسته‌بندی را به‌جای جاوا برای پشتیبان NDK به‌عنوان پایدار علامت‌گذاری می‌کند.

برای استفاده از این parcelable:

  • باید ndk_header را مشخص کنید.
  • باید کتابخانه NDK داشته باشید که parcelable را مشخص کند و کتابخانه باید در کتابخانه کامپایل شود. برای مثال، در سیستم ساختار اصلی در واحد cc_*، از static_libs یا shared_libs استفاده کنید. برای aidl_interface، کتابخانه را در additional_shared_libraries در Android.bp اضافه کنید.

JavaOnlyStableParcelable

JavaOnlyStableParcelable اعلامیه بسته‌شدنی (نه تعریف) را به‌عنوان پایدار علامت‌گذاری می‌کند تا بتواند از انواع AIDL پایدار دیگر ارجاع داده شود.

AIDL پایدار مستلزم آن است که همه انواع تعریف‌شده توسط کاربر پایدار باشند. برای parcelable، پایدار بودن مستلزم این است که فیلدهای آن به‌طور صریح در فایل منبع AIDL شرح داده شود:

parcelable Data { // Data is a structured parcelable.
    int x;
    int y;
}

parcelable AnotherData { // AnotherData is also a structured parcelable
    Data d; // OK, because Data is a structured parcelable
}

اگر parcelable بدون ساختار باشد (یا فقط اعلام شده باشد)، نمی‌توان به آن ارجاع داد:

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable به شما امکان می‌دهد وقتی parcelable مورد ارجاع شما به‌عنوان بخشی از کیت توسعه نرم‌افزار Android به‌طور ایمن دردسترس است، بررسی را لغو کنید:

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

‫JavaDerive به‌طور خودکار روش‌هایی برای انواع بسته‌شدنی در پشتیبان Java تولید می‌کند:

@JavaDerive(equals = true, toString = true)
parcelable Data {
  int number;
  String str;
}

این گزارمان برای کنترل آنچه باید تولید شود به پارامترهای اضافی نیاز دارد. پارامترهای پشتیبانی‌شده عبارت‌اند از:

  • ‫equals=true روش‌های equals و hashCode را تولید می‌کند.
  • ‫toString=true روش toString را تولید می‌کند که نام نوع و فیلدها را چاپ می‌کند، برای مثال، Data{number: 42, str: foo}.

JavaDefault (منسوخ‌شده)

‫JavaDefault، که در Android 13 اضافه شده است، کنترل می‌کند که آیا پشتیبانی نسخه‌بندی پیاده‌سازی پیش‌فرض تولید می‌شود یا نه (برای setDefaultImpl). این پشتیبانی دیگر به‌طور پیش‌فرض تولید نمی‌شود تا در فضا صرفه‌جویی شود.

JavaPassthrough

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

این حاشیه‌نویسی‌ها در AIDL:

@JavaPassthrough(annotation="@android.annotation.Alice")
@JavaPassthrough(annotation="@com.android.Alice(arg=com.android.Alice.Value.A)")

در کد جاوا تولیدشده به این تبدیل می‌شود:

@android.annotation.Alice
@com.android.Alice(arg=com.android.Alice.Value.A)

مقدار پارامتر annotation مستقیماً منتشر می‌شود. گردآورنده AIDL مقدار پارامتر را بررسی نمی‌کند. اگر هرگونه خطای نحوی در سطح Java وجود داشته باشد، توسط کامپایلر AIDL شناسایی نخواهد شد، بلکه توسط کامپایلر Java شناسایی می‌شود.

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

RustDerive

‫RustDerive به‌طور خودکار ویژگی‌ها را برای انواع Rust تولیدشده پیاده‌سازی می‌کند.

این گزارمان برای کنترل آنچه باید تولید شود به پارامترهای اضافی نیاز دارد. پارامترهای پشتیبانی‌شده عبارت‌اند از:

  • Copy=true
  • Clone=true
  • Ord=true
  • PartialOrd=true
  • Eq=true
  • PartialEq=true
  • Hash=true

برای توضیحات این ویژگی‌ها، به مستندات Rust مراجعه کنید.

اندازه ثابت

FixedSize یک parcelable ساختاریافته را به‌عنوان اندازه ثابت علامت‌گذاری می‌کند. پس‌از علامت‌گذاری، نمی‌توانید فیلدهای جدیدی به parcelable اضافه کنید. همه فیلدهای parcelable باید از نوع اندازه ثابت باشند، ازجمله انواع اولیه، تعداد، آرایه‌های اندازه ثابت، و parcelableهای دیگری که با FixedSize علامت‌گذاری شده‌اند.

‫FixedSize شیء دارای اندازه و تراز پایدار در پسای ndk هستند.

نوع اندازه (بایت) تراز (بایت)
boolean 1 1
byte 1 1
char 2 2
int 4 4
long 8 8
float 4 4
double 8 8
parcelable اندازه کل همه فیلدها بزرگ‌ترین تراز همه فیلدها
union بزرگ‌ترین اندازه همه فیلدها بزرگ‌ترین تراز همه فیلدها
enum اندازه نوع پشتیبان تراز کردن نوع پشتیبان
T[N] (آرایه با اندازه ثابت) اندازه T * N تراز T
String, IBinder, FileDescriptor, ParcelFileDescriptor N/A N/A

توصیف‌گر

‫Descriptor به‌طور اجباری مشخص‌کننده میانای یک میانای کاربری را مشخص می‌کند:

package android.foo;

@Descriptor(value="android.bar.IWorld")
interface IHello {...}

توصیف‌گر این میانای کاربری android.bar.IWorld است. اگر گزارمان Descriptor وجود نداشته باشد، توصیف‌گر android.foo.IHello خواهد بود.

این گزینه برای تغییر نام واسطی که قبلاً منتشر شده است مفید است. اگر توصیف‌گر میانای تغییرنام‌یافته را با توصیف‌گر میانای قبل‌از تغییرنام یکسان کنید، این دو میانا می‌توانند با یکدیگر ارتباط برقرار کنند.

‫@hide در نظرات

گردآورنده AIDL @hide را در نظرات تشخیص می‌دهد و آن را به برونداد جاوا برای metalava ارسال می‌کند تا آن را دریافت کند. این نظر کمک می‌کند تا مطمئن شوید که سیستم ساخت Android تشخیص می‌دهد که میاناهای برنامه‌سازی کاربردی AIDL میاناهای برنامه‌سازی کاربردی کیت توسعه نرم‌افزار نیستند.

‫deprecated@ در نظرات

گردآورنده AIDL @deprecated را در نظرات به‌عنوان برچسبی برای شناسایی نهاد AIDL که دیگر نباید استفاده شود تشخیص می‌دهد:

interface IFoo {
  /** @deprecated use bar() instead */
  void foo();
  void bar();
}

هر زیرینه نهادهای منسوخ‌شده را با یک شرح یا مشخصه مختص زیرینه علامت‌گذاری می‌کند تا اگر کد کارخواه به نهادهای منسوخ‌شده ارجاع دهد، به آن هشدار داده شود. برای مثال، @Deprecated گزارمان و @deprecated برچسب به کد تولیدشده Java پیوست شده است.