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

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

سینتکس آن مشابه جاوا است:

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

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

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

@AnnotationName AidlEntity

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

در اینجا لیستی از حاشیه‌نویسی‌های از پیش تعریف‌شده‌ی AIDL آمده است:

حاشیه‌نویسی‌ها در نسخه اندروید اضافه شد
nullable ۷
utf8InCpp ۷
VintfStability ۱۱
UnsupportedAppUsage ۱۰
Hide ۱۱
Backing ۱۱
NdkOnlyStableParcelable ۱۴
JavaOnlyStableParcelable ۱۱
JavaDerive ۱۲
JavaPassthrough ۱۲
FixedSize ۱۲
Descriptor ۱۲

قابل تهی‌سازی

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

این حاشیه‌نویسی فقط می‌تواند به انواع بازگشتی متد، پارامترهای متد و فیلدهای parcelable پیوست شود.

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

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

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

حاشیه‌نویسی‌ها نمی‌توانند به انواع اولیه (primitive types) متصل شوند. خطای زیر رخ می‌دهد.

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

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

در بک‌اند CPP، @nullable T در اندروید ۱۱ یا پایین‌تر به std::unique_ptr<T> و در اندروید ۱۲ یا بالاتر به std::optional<T> نگاشت می‌شود.

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

در backend زبان 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 برای اندروید ۱۱ یا پایین‌تر) نگاشت می‌شود.

یک استثنا برای این نگاشت وجود دارد. وقتی T برابر IBinder یا یک رابط AIDL باشد، @nullable برای همه backendها به جز Rust غیرفعال است. به عبارت دیگر، هم @nullable IBinder و هم IBinder به طور یکسان به android::sp<IBinder> نگاشت می‌شوند، که از قبل nullable است زیرا یک اشاره‌گر قوی است (CPP reads همچنان nullability را اعمال می‌کند، اما نوع آن هنوز android::sp<IBinder> است). در Rust، این نوع‌ها فقط در صورتی nullable هستند که با @nullable حاشیه‌نویسی شوند. در صورت حاشیه‌نویسی، به Option<T> نگاشت می‌شوند.

از اندروید ۱۳ به بعد، می‌توان از @nullable(heap=true) برای فیلدهای parcelable جهت مدل‌سازی انواع بازگشتی استفاده کرد. @nullable(heap=true) را نمی‌توان با پارامترهای متد یا انواع بازگشتی استفاده کرد. هنگامی که با آن حاشیه‌نویسی می‌شود، فیلد به یک std::unique_ptr<T> تخصیص‌یافته به heap در backendهای CPP/NDK نگاشت می‌شود. @nullable(heap=true) در backend جاوا no-op است.

utf8InCpp

utf8InCpp اعلام می‌کند که یک String برای backend CPP با فرمت UTF8 نمایش داده می‌شود. همانطور که از نامش پیداست، این حاشیه‌نویسی برای سایر backendها قابل استفاده نیست. به طور خاص، String همیشه در backend جاوا UTF16 و در backend NDK UTF8 است.

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

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

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

پایداری وینت‌اف

VintfStability اعلام می‌کند که یک نوع تعریف‌شده توسط کاربر (interface، 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 نشان می‌دهد که نوع AIDL حاشیه‌نویسی‌شده بخشی از رابط غیر SDK است که برای برنامه‌های قدیمی قابل دسترسی بوده است. برای اطلاعات بیشتر در مورد APIهای پنهان ، به محدودیت‌های رابط‌های غیر SDK مراجعه کنید.

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

// 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 فرض می‌شود که برای backend CPP به int8_t نگاشت می‌شود.

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

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

NdkOnlyStableParcelable

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

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

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

JavaOnlyStableParcelable

JavaOnlyStableParcelable یک اعلان parcelable (نه تعریف) را به عنوان پایدار علامت‌گذاری می‌کند تا بتوان از سایر انواع 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 مورد ارجاع شما از قبل به عنوان بخشی از SDK اندروید به طور ایمن در دسترس است، بررسی را لغو کنید.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

جاوا درایو

JavaDerive به طور خودکار متدهایی را برای انواع parcelable در backend جاوا تولید می‌کند.

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

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

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

جاواپیش‌فرض

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

جاوا پس‌راو

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

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

این حاشیه‌نویسی می‌تواند به هر موجودیت AIDL متصل شود. این حاشیه‌نویسی برای backend های غیر جاوایی، گزینه‌ای نیست.

RustDeriv

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

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

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

برای توضیحات این ویژگی‌ها، به https://doc.rust-lang.org مراجعه کنید.

اندازه ثابت

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

توصیفگر

Descriptor به اجبار توصیفگر رابط یک رابط را مشخص می‌کند.

package android.foo;

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

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

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

@hide در نظرات

کامپایلر AIDL، @hide در کامنت‌ها تشخیص می‌دهد و آن را به خروجی جاوا ارسال می‌کند تا توسط metalava دریافت شود. این کامنت تضمین می‌کند که سیستم ساخت اندروید می‌داند که APIهای AIDL، APIهای SDK نیستند.

@deprecated در نظرات

کامپایلر AIDL، @deprecated در نظرات به عنوان یک برچسب برای شناسایی یک موجودیت AIDL که دیگر نباید استفاده شود، تشخیص می‌دهد.

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

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