הערות ב-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 מצהיר שאפשר להשתמש בסוג שהוגדר על ידי משתמש (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, הפונקציה הזו פולטת סוג של Enumeration Class ב-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 יציבה, כל הסוגים שהוגדרו על ידי המשתמש צריכים להיות יציבים. כדי ש-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 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.

גודל קבוע

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.