על סמך קובץ ממשק HIDL, הקצה העורפי של Java HIDL יוצר ממשקי Java, קוד Stub וקוד Proxy. הוא תומך בכל סוגי ה-HIDL סקלריים ([u
]int
{8,16,32,64}_t, float, double,
ו-enum
s), וגם במחרוזות, בממשקים, בסוגי safe_union, בסוגי struct, במערכים ובווקטורים של סוגי HIDL נתמכים. הקצה העורפי של Java HIDL לא תומך בסוגים של יוניון או בסוגים של FMQ. ב-Android 11 נוספה תמיכה בסוגי memory
ו-handle
.
מאחר שסביבת זמן הריצה של Java לא תומכת באופן מקורי במושג של מספרים שלמים ללא סימן, כל הטיפוסים ללא סימן (וה-enums שמבוססים עליהם) מטופלים בשקט כמקבילים שלהם עם סימן, כלומר uint32_t
הופך ל-int
בממשק Java. לא מתבצעת המרה של הערכים. המטמיע בצד Java צריך להשתמש בערכים החתומים כאילו הם לא חתומים.
טיפוסים בני מנייה (enum)
Enums לא יוצרים כיתות enum של Java, אלא מתרגמים אותן לכיתות פנימיות שמכילות הגדרה של קבוע סטטי לכל מקרה של enum. אם סוג המאפיין נגזר מסוג מאפיין אחר, הוא יורש את סוג האחסון של הסוג הזה. ספירה עוקבת שמבוססת על סוג של מספר שלם ללא סימן נכתבת מחדש בתור המקבילה שלה עם סימן. מכיוון שהסוג הבסיסי הוא פרימיטיבי, ערך ברירת המחדל של שדות או משתני enum הוא אפס, גם אם אין מונה אפס.
לדוגמה, SomeBaseEnum
עם סוג uint8_t
:
enum SomeBaseEnum : uint8_t { foo = 3 }; enum SomeEnum : SomeBaseEnum { quux = 33, goober = 127 };
… הופך ל-:
public final class SomeBaseEnum { public static final byte foo = 3; } public final class SomeEnum { public static final byte foo = 3; public static final byte quux = 33; public static final byte goober = 127; }
וגם:
enum SomeEnum : uint8_t { FIRST_CASE = 10, SECOND_CASE = 192 };
… נכתב מחדש כך:
public final class SomeEnum { static public final byte FIRST_CASE = 10; // no change static public final byte SECOND_CASE = -64; }
מיתרים
String
ב-Java הוא utf-8 או utf-16, אבל הוא מומר ל-utf-8 בתור הטיפוס הנפוץ של HIDL במהלך ההעברה. בנוסף, הערך של String
חייב להיות לא null כשהוא מועבר ל-HIDL.
טיפול בזיכרון
ב-Android 11 יש תמיכה ב-Java לסוגי handle
ו-memory
. הם מתורגמים ל-android.os.NativeHandle
ול-android.os.HidlMemory
, בהתאמה. אחיזה (handle) של null נחשבת לתקינה, אבל זיכרון של null לא נחשב לתקין.
בקוד השרת שנוצר, הארגומנטים של זיכרון ושל טיפולן תקפים רק בהיקף של קריאת ה-method. אם רוצים להאריך את משך החיים שלהם בהטמעה בשרת, צריך ליצור להם עותק באמצעות השיטות dup()
המתאימות. אפשר להשתמש במכונה שהוחזרה גם אחרי הקריאה לשיטה, וצריך לסגור אותה כראוי בסיום השימוש.
בקוד הלקוח שנוצר, אין צורך ליצור עותקים כפולים של מזהים (handles) ומופעי זיכרון שנשלחים כארגומנטים של קלט של השיטה שנקראת, או לשמור על תקינות שלהם אחרי שהשיטה חוזרת. עם זאת, כאשר משתמשים ב-handle ובמכונות זיכרון שמתקבלים כארגומנטים של פלט, הקוד שנוצר באופן אוטומטי יוצר עותקים כפולים שלהם, וצריך לסגור אותם כראוי בסיום השימוש. זה נכון גם אם הארגומנטים האלה מופיעים כערכים שמוחזרים מהשיטה (במקרה של ערך מוחזר יחיד) וגם אם הם מופיעים בסגנון של קריאה חוזרת אסינכרוני (במקרה של מספר ערכים מוחזרים).
מידע נוסף על כפילויות וסגירה זמין במסמכי התיעוד של הכיתות ב-Java.
מערכים וקטורים
מערכים מתורגמים למערכים של Java, ווקטורים מתורגמים ל-ArrayList<T>
, כאשר T הוא סוג האובייקט המתאים, שעשוי לעטוף סוגים סקלריים כמו vec<int32_t> =>
ArrayList<Integer>
). לדוגמה:
takeAnArray(int32_t[3] array); returnAVector() generates (vec<int32_t> result);
… הופך ל-:
void takeAnArray(int[] array); ArrayList<Integer> returnAVector();
מבנים
המבנים מתורגמים לכיתות Java עם פריסה דומה. לדוגמה:
struct Bar { vec<bool> someBools; }; struct Foo { int32_t a; int8_t b; float[10] c; Bar d; };
… הופך ל-:
class Bar { public final ArrayList<Boolean> someBools = new ArrayList(); }; class Foo { public int a; public byte b; public final float[] c = new float[10]; public final Bar d = new Bar(); }
סוגי נתונים שהוגדרו
לכל סוג ברמה העליונה שמוצהר ב-types.hal
מוקצה קובץ פלט משלו עם הסיומת .java (כפי שנדרש ב-Java). לדוגמה, קובץ types.hal
הבא יוצר שני קבצים נוספים (Foo.java ו-Bar.java):
struct Foo { ... }; struct Bar { ... struct Baz { }; ... };
ההגדרה של Baz נמצאת בכיתה פנימית סטטית של Bar (ב-Bar.java
).