הערות ב-AIDL

ב-AIDL יש תמיכה בהערות שמספקות למהדר AIDL מידע נוסף על הרכיב המתויג, שמשפיע גם על קוד הסטאב שנוצר.

התחביר דומה לזה של Java:

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

כאן, AnnotationName הוא שם ההערה ו-AidlEntity הוא ישות AIDL כמו interface Foo,‏ void method() או int arg. ההערה מצורפת לישות שמופיעה אחריה.

בחלק מההערות אפשר להגדיר ארגומנטים בתוך הסוגריים, כפי שמוצג למעלה. בהערות שאין להן ארגומנט, אין צורך בסוגריים. לדוגמה:

@AnnotationName AidlEntity

ההערות האלה לא זהות להערות ב-Java, אבל הן נראות דומות מאוד. המשתמשים לא יכולים להגדיר הערות AIDL בהתאמה אישית, כל ההערות מוגדרות מראש. חלק מההערות משפיעות רק על קצה עורפי מסוים, והן לא פועלות בקצוות עורפיים אחרים. לכל סוג יש הגבלות שונות לגבי המקומות שבהם אפשר לצרף אותו.

זוהי רשימת ההערות המוגדרות מראש ב-AIDL:

הערות נוספה בגרסה ל-Android
nullable 7
utf8InCpp 7
VintfStability 11
UnsupportedAppUsage 10
Hide 11
Backing 11
NdkOnlyStableParcelable 14
JavaOnlyStableParcelable 11
JavaDerive 12
JavaPassthrough 12
FixedSize 12
Descriptor 12

nullable

הערך 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. הסיבה לכך היא שב-Java, כל סוגי הנתונים שאינם פרימיטיביים מועברים באמצעות הפניה, שיכולה להיות null.

בקצה העורפי של ה-CPP, הערך @nullable T ממופה לערך std::unique_ptr<T> ב-Android 11 ומטה, ולערך std::optional<T> ב-Android 12 ואילך.

בקצה העורפי של 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 לא מבצעת פעולה כלשהי בכל הקצוות העורפיים, מלבד Rust. במילים אחרות, גם @nullable IBinder וגם IBinder ממפים באופן שווה ל-android::sp<IBinder>, שכבר יכול להכיל ערך null כי הוא הצבעה חזקה (קריאות ב-CPP עדיין אוכפות את היכולת להכיל ערך null, אבל הסוג עדיין android::sp<IBinder>). ב-Rust, הסוגים האלה הם nullable רק אם מסמנים אותם ב-@nullable. אם נוספו להן הערות, הן ממופות לערך Option<T>.

החל מ-Android 13, אפשר להשתמש ב-@nullable(heap=true) בשדות שניתן לחלק כדי ליצור מודלים של סוגים רפלקסיביים. אי אפשר להשתמש ב-@nullable(heap=true) עם פרמטרים של שיטות או עם סוגי החזרים. כשמציינים את ההערה הזו, השדה ממופה להפניה std::unique_ptr<T> שמוקצית ב-heap בקצוות העורפי של CPP/NDK. @nullable(heap=true) הוא פעולה ללא תוצאה בקצה העורפי של Java.

utf8InCpp

utf8InCpp מצהיר ש-String מיוצג בפורמט UTF8 לקצה העורפי של CPP. כפי שרואים מהשם, ההערה לא מבוצעת בקצוות עורפיים אחרים. באופן ספציפי, String הוא תמיד UTF16 בקצה העורפי של Java ו-UTF8 בקצה העורפי של NDK.

אפשר לצרף את ההערה הזו בכל מקום שבו אפשר להשתמש בסוג String, כולל ערכי החזרה, פרמטרים, הצהרות על קבועים ושדות שניתנים להעברה (Parcelable).

לקצה העורפי של ה-CPP, הערך @utf8InCpp String ב-AIDL ממופה ל-std::string, ואילו הערך String ללא ההערה ממופה ל-android::String16, שבו נעשה שימוש ב-UTF16.

חשוב לזכור שההערה utf8InCpp לא משנה את האופן שבו מחרוזות מועברות דרך החיבור. מחרוזות מועברות תמיד כ-UTF16 באינטרנט. מחרוזת עם הערות של utf8InCpp מומרת ל-UTF16 לפני שהיא מועברת. כשמתקבלת מחרוזת, היא מומרת מ-UTF16 ל-UTF8 אם היא סומנה בתווית utf8InCpp.

VintfStability

VintfStability מצהיר שאפשר להשתמש בסוג שהוגדר על ידי משתמש (interface,‏ parcelable ו-enum) בכל הדומיינים של המערכת והספק. מידע נוסף על יכולת פעולה הדדית בין ספקי מערכות זמין במאמר AIDL ל-HALs.

ההערה לא משנה את החתימה של הסוג, אבל כשמגדירים אותה, המכונה של הסוג מסומנת כיציבה כדי שתוכל לעבור את תהליכי הספק והמערכת.

אפשר לצרף את ההערה רק להצהרות על סוגים שהוגדרו על ידי משתמשים, כפי שמוצג כאן:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

כשסוג מסוים מסומן ב-VintfStability, צריך לסמן גם כל סוג אחר שמצוין בסוג הזה. בדוגמה הבאה, צריך להוסיף הערה VintfStability גם ל-Data וגם ל-IBar.

@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 שהיה נגיש לאפליקציות מדור קודם. מידע נוסף על ממשקי ה-API המוסתרים זמין במאמר הגבלות על ממשקים שאינם SDK.

ההערה UnsupportedAppUsage לא משפיעה על ההתנהגות של הקוד שנוצר. האנוטציה מוסיפה את האנוטציה של Java עם אותו שם רק לכיתה ב-Java שנוצרה.

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

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

זוהי פעולה ללא תוצאה (no-op) לקצוות עורפיים שאינם Java.

גיבוי

ההערה Backing מציינת את סוג האחסון של סוג enum ב-AIDL.

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

בקצה העורפי של ה-CPP, הפונקציה הזו פולטת סוג של enum ב-C++ מסוג int32_t.

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

אם משמיטים את ההערה, ההנחה היא ש-type הוא byte, שממופה ל-int8_t לקצה העורפי של ה-CPP.

אפשר להגדיר את הארגומנט type רק לסוגי המספרים השלמים הבאים:

  • byte (רוחב 8 ביט)
  • int (רוחב 32 ביט)
  • long (רוחב 64 ביט)

NdkOnlyStableParcelable

NdkOnlyStableParcelable מסמנת הצהרה על תכונה שניתנת להעברה (לא הגדרה) כיציבה, כדי שניתן יהיה להפנות אליה מסוגים יציבים אחרים של AIDL. זהו תיוג דומה ל-JavaOnlyStableParcelable, אבל הערך NdkOnlyStableParcelable מסמנים הצהרה על תכונה שניתנת לחלוקה כיציבה לקצה העורפי של NDK במקום לג'אווה.

כדי להשתמש ב-Parcelable הזה:

  • חובה לציין את ndk_header.
  • צריכה להיות לכם ספריית NDK שמציינת את ה-Parcelable, והספרייה צריכה להיות מתומצתת בספרייה. לדוגמה, במערכת הליבה ליצירת גרסאות build במודול cc_*, משתמשים ב-static_libs או ב-shared_libs. עבור aidl_interface, מוסיפים את הספרייה בקטע additional_shared_libraries ב-Android.bp.

JavaOnlyStableParcelable

JavaOnlyStableParcelable מסמנת הצהרה על תכונה שניתנת להעברה (לא הגדרה) כיציבה, כדי שניתן יהיה להפנות אליה מסוגים יציבים אחרים של AIDL.

כדי ליצור גרסת AIDL יציבה, כל הסוגים שהוגדרו על ידי המשתמש צריכים להיות יציבים. כדי ש-Parcelables יהיו יציבים, צריך לתאר את השדות שלהם באופן מפורש בקובץ המקור של 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 SDK.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive יוצר באופן אוטומטי שיטות לסוגים של parcelable בקצה העורפי של 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 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)

בקוד Java שנוצר.

הערך של הפרמטר annotation נפלט ישירות. המהדר של AIDL לא בודק את ערך הפרמטר. אם יש שגיאת תחביר ברמת Java, היא לא תזוהה על ידי המהדר של AIDL אלא על ידי המהדר של Java.

אפשר לצרף את ההערה הזו לכל ישות AIDL. ההערה הזו לא מבוצעת בקצוות עורפיים שאינם Java.

RustDerive

RustDerive מטמיע באופן אוטומטי מאפיינים לסוגים שנוצרו ב-Rust.

כדי לשלוט במה שייווצר, צריך להוסיף פרמטרים להערה. הפרמטרים הנתמכים הם:

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

הסברים על המאפיינים האלה זמינים בכתובת https://doc.rust-lang.org.

FixedSize

FixedSize מסמנת רכיב parcelable מובנה כרכיב בגודל קבוע. אחרי הסימון, לא תהיה אפשרות להוסיף שדות חדשים ל-Parcelable. כל השדות של ה-Parcelable חייבים להיות גם מסוגים בגודל קבוע, כולל סוגים פרימיטיביים, מערכים בגודל קבוע ו-Parcelables אחרים שמסומנים ב-FixedSize.

הדבר לא מבטיח תאימות בין ערכים שונים של ביטים, ואין להסתמך עליו בתקשורת עם ערכים שונים של ביטים.

תיאור

Descriptor מציין בכפייה את מתאר הממשק של ממשק.

package android.foo;

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

התיאור של הממשק הזה הוא android.bar.IWorld. אם ההערה Descriptor חסרה, התיאור יהיה android.foo.IHello.

אפשר להשתמש באפשרות הזו כדי לשנות את השם של ממשק שכבר פורסם. כך שני הממשקים יוכלו לתקשר ביניהם.

@hide בתגובות

המהדר של AIDL מזהה את @hide בתגובות ומעביר אותו לפלט של Java כדי ש-metalava תוכל לאסוף אותו. התגובה הזו מוודאת שמערכת ה-build של Android יודעת שממשקי AIDL API הם לא ממשקי SDK API.

@deprecated בתגובות

המהדר של AIDL מזהה את הערך @deprecated בתגובות בתור תג לזיהוי ישות AIDL שאין להשתמש בה יותר.

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

כל קצה עורפי מסומן ישויות שהוצאו משימוש באמצעות הערה או מאפיין ספציפיים לקצה העורפי, כדי שיופיע אזהרה בקוד הלקוח אם הוא מפנה לישויות שהוצאו משימוש. לדוגמה, ההערה @Deprecated והתג @deprecated מצורפים לקוד שנוצר ב-Java.