שפת AIDL

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

חבילה

כל קובץ AIDL מתחיל בחבילה אופציונלית שתואמת לשמות החבילות בקצוות העורפי השונים. הצהרת החבילה נראית כך:

    package my.package;

בדומה ל-Java, קובצי AIDL חייבים להיות במבנה תיקיות שתואם לחבילה שלהם. קבצים עם החבילה my.package חייבים להיות בתיקייה my/package/.

סוגים

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

הערות

יש כמה חלקים בשפת AIDL שתומכים בהערות. רשימה של הערות ואיפה אפשר להחיל אותן מפורטת במאמר הערות ב-AIDL.

ייבוא

כדי להשתמש בסוגי נתונים שהוגדרו בממשקים אחרים, צריך קודם להוסיף יחסי תלות במערכת ה-build. במודולים של Soong‏ cc_* ו-java_*, שבהם קבצים מסוג .aidl משמשים ישירות ב-srcs ב-builds של פלטפורמת Android, אפשר להוסיף ספריות באמצעות השדה aidl: { include_dirs: ... }. מידע נוסף על ייבוא באמצעות aidl_interface זמין כאן.

ייבוא נראה כך:

    import some.package.Foo;  // explicit import

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

    import Foo;  // same as my.package.Foo

הגדרת סוגים

בדרך כלל, קובצי AIDL מגדירים סוגים שמשמשים כממשק.

ממשקים

דוגמה לממשק AIDL:

    interface ITeleport {
        // Location defined elsewhere
        void teleport(Location baz, float speed);
        String getName();

        // ITeleportCallback defined elsewhere
        void methodWithCallback(ITeleportCallback callback);

        // ITeleportSession defined elsewhere
        ITeleportSession getASubInterface();
    }

ממשק מגדיר אובייקט עם סדרה של שיטות. השיטות יכולות להיות oneway (oneway void doFoo()) או סינכרוניות. אם ממשק מוגדר בתור oneway (oneway interface ITeleport {...}), כל השיטות בו הן oneway באופן סמלי. שיטות חד-כיווניות מופעלות באופן אסינכרוני ולא ניתן להחזיר בהן תוצאה. שיטות חד-כיווניות מאותו חוט לאותו מקשר (binder) גם מתבצעות ברצף (אבל יכול להיות שבחוטים שונים). במאמר ניהול שרשור של קצות עורפי של AIDL מוסבר איך מגדירים את השרשור.

בעזרת Binder אפשר לשתף ממשקים רבים ואובייקטים של Binder באמצעות ממשקי Binder. ממשקי AIDL משתמשים לעיתים קרובות בקריאות חזרה (callbacks) כחלק מבקריאות ל-method, כמו ב-ITeleportCallback בדוגמה הקודמת. אפשר לעשות שימוש חוזר באובייקטים של קריאה חוזרת בין קריאות לאותה שיטה או בין קריאות לשיטות שונות. שימוש נפוץ נוסף בסוגי ממשקים הוא להחזרת ממשקי משנה או אובייקטים של סשנים משיטות, כמו השיטה with‏ ITeleportSession בדוגמה הקודמת. ההטמעה הזו מאפשרת להכניס לתוך קופסה ממשקי API שונים, ברמת ה-API או על סמך מצב בסביבת זמן הריצה. לדוגמה, סשן יכול לייצג בעלות על משאב מסוים. כשמעבירים ממשקים כמה פעמים או מחזירים אותם ללקוח או לשרת שמהם הם הגיעו, הם תמיד שומרים על שוויון של המצביע של אובייקט ה-binder הבסיסי.

שיטות יכולות לכלול אפס ארגומנטים או יותר. ארגומנטים לשיטות יכולים להיות in,‏ out או inout. במאמר כיווניות של קצוות עורפיים ב-AIDL מוסבר איך הכיוון משפיע על סוגי הארגומנטים.

Parcelables

Parcelables בהתאמה אישית לקצוות עורפיים של AIDL

ב-Android מגרסה 10 ואילך יש תמיכה בהגדרות של Parcelable ישירות ב-AIDL. סוג ה-Parcelable הזה נקרא Parcelable מובנה. למידע נוסף על הקשר בין קוד AIDL מובנה לקוד AIDL יציב במהדר AIDL ובמערכת ה-build שלנו, ראו קוד AIDL מובנה לעומת קוד AIDL יציב.

לדוגמה:

    package my.package;

    import my.package.Boo;

    parcelable Baz {
        @utf8InCpp String name = "baz";
        Boo boo;
    }

איגודים

ב-Android מגרסה 12 ואילך יש תמיכה בהצהרות על יוניונים מתויגים. לדוגמה:

    package my.package;

    import my.package.FooSettings;
    import my.package.BarSettings;

    union Settings {
        FooSettings fooSettings;
        BarSettings barSettings;
        @utf8InCpp String str;
        int number;
    }

פרטים ספציפיים לקצה העורפי זמינים במאמר איחודים של קצות עורפי ב-AIDL.

טיפוסים בני מנייה (enum)

ב-Android מגרסה 11 ואילך יש תמיכה בהצהרות על ערכים מוגדרים מראש. לדוגמה:

    package my.package;

    enum Boo {
        A = 1 * 4,
        B = 3,
    }

הצהרות על סוגים בתצוגת עץ

ב-Android מגרסה 13 ואילך יש תמיכה בהצהרות על סוגי עץ. לדוגמה:

    package my.package;

    import my.package.Baz;

    interface IFoo {
        void doFoo(Baz.Nested nested);  // defined in my/package/Baz.aidl
        void doBar(Bar bar);            // defined below

        parcelable Bar { ... }          // nested type definition
    }

קבועים

ממשקי AIDL, רכיבי Parcelable וישויות איחוד בהתאמה אישית יכולים גם להכיל ערכים קבועים של מחרוזות ומספרים שלמים, כמו:

    const @utf8InCpp String HAPPY = ":)";
    const String SAD = ":(";
    const byte BYTE_ME = 1;
    const int ANSWER = 6 * 7;

ביטויים קבועים

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

ליטרלים של true ו-false מייצגים ערכים בוליאניים. ערכים עם . אבל ללא סיומת, כמו 3.8, נחשבים כערכים כפולים. לערכים מסוג float יש את הסיומת f, למשל 2.4f. ערך שלם עם הסיומת l או L מציין ערך באורך 64 ביט. אחרת, הערכים של האינטגרלים מקבלים את הסוג הקטן ביותר עם סימן שמאפשר לשמור את הערך, בין 8 ביט (בייט), 32 ביט (int) ו-64 ביט (long). לכן, 256 נחשב ל-int, אבל 255 + 1 מתרוקן ומקבל את הערך byte 0. ערכים הקסדצימליים, כמו 0x3, מפורשים קודם בתור הסוג הקטן ביותר ללא סימן שמשמרת את הערך בין 32 סיביות ל-64 סיביות, ולאחר מכן מפורשים מחדש בתור ערכים ללא סימן. לכן, הערך של 0xffffffff הוא int-1. החל מ-Android 13, אפשר להוסיף את הסיומת u8 למשתנים קבועים, כמו 3u8, כדי לייצג ערך byte. הסיומת הזו חשובה כדי שחישוב כמו 0xffu8 * 3 יפורש כ--3 מסוג byte, בעוד ש-0xff * 3 הוא 765 מסוג int.

לאופרטורים הנתמכים יש סמנטיקה של C++‎ ו-Java. הסדר של האופרטורים הבינאריים הוא || && | ^ & == != < > <= >= << >> + - * / %, מהעדיפות הנמוכה ביותר עד הגבוהה ביותר. אופרטורים חד-ערך הם + - ! ~.