ב-Android 8.0, הארכיטקטורה של Android OS עוצבה מחדש כדי להגדיר ממשקים ברורים בין פלטפורמת Android שאינה תלויה במכשיר לבין קוד ספציפי למכשיר ולספק. כבר הוגדרו ב-Android ממשקים רבים כאלה בצורת ממשקי HAL, שהוגדרו ככותרות C ב-hardware/libhardware
. HIDL מחליף את ממשקי ה-HAL האלה בממשקים יציבים עם גרסאות, שיכולים להיות ב-Java (כפי שמתואר בהמשך) או בממשקי HIDL בצד הלקוח ובצד השרת ב-C++.
הממשקים של HIDL מיועדים לשימוש בעיקר מקוד מקורי, ולכן ה-HIDL מתמקד ביצירה אוטומטית של קוד יעיל ב-C++. עם זאת, ממשקי HIDL חייבים להיות זמינים גם לשימוש ישירות מ-Java, כי לחלק מהתת-מערכות של Android (כמו Telephony) יש ממשקי HIDL של Java.
בדפים שבקטע הזה מתוארת חזית Java לממשקי HIDL, מוסבר איך יוצרים שירותים, רושמים אותם ומשתמשים בהם, ומוסבר איך ממשקי HAL ולקוחות HAL שנכתבו ב-Java מקיימים אינטראקציה עם מערכת ה-RPC של HIDL.
דוגמה ללקוח
זוהי דוגמה ללקוח של ממשק IFoo
בחבילה android.hardware.foo@1.0
שמירשם בתור שם השירות default
ושירות נוסף עם שם השירות המותאם אישית second_impl
.
הוספת ספריות
אם רוצים להשתמש בספרייה, צריך להוסיף יחסי תלות לספריית ה-stub של HIDL המתאימה. בדרך כלל מדובר בספרייה סטטית:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
אם אתם יודעים שכבר אתם מושכים יחסי תלות בספריות האלה, תוכלו גם להשתמש בקישור משותף:
// in Android.bp libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
שיקולים נוספים להוספת ספריות ב-Android 10
אם יש לכם אפליקציית מערכת או אפליקציית יצרן שמטרגטת את Android מגרסה 10 ואילך, תוכלו לכלול את הספריות האלה באופן סטטי. אפשר גם להשתמש (רק) ב-HIDL classes מ-JARs מותאמים אישית שמותקנים במכשיר, עם ממשקי Java API יציבים שזמינים באמצעות המנגנון הקיים של uses-library
לאפליקציות מערכת. הגישה השנייה חוסכת מקום במכשיר. פרטים נוספים זמינים במאמר הטמעת ספריית Java SDK. באפליקציות ישנות יותר, ההתנהגות הישנה נשמרת.
החל מ-Android 10, זמינות גם גרסאות 'שטוחות' של הספריות האלה. הן כוללות את הכיתה הרלוונטית, אבל לא את הכיתות התלויות. לדוגמה, android.hardware.foo-V1.0-java-shallow
כולל כיתות בחבילה foo, אבל לא כולל כיתות ב-android.hidl.base-V1.0-java
, שמכילה את מחלקת הבסיס של כל ממשקי HIDL. אם אתם יוצרים ספרייה שכבר יש בה את הכיתות הבסיסיות של הממשק המועדף כיחס תלות, תוכלו להשתמש באפשרויות הבאות:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java-shallow", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow
בנוסף, ספריות הבסיס והמנהל של HIDL כבר לא זמינות ב-classpath של האפליקציות (בעבר, הן שימשו לפעמים כ-API מוסתר, בגלל מטען הכיתבים של Android שמשתמש ב-delegate-first). במקום זאת, הם הועברו למרחב שמות חדש עם jarjar
, ואפליקציות שמשתמשות בהם (באופן הכרחי אפליקציות פרטיות) צריכות להכיל עותקים נפרדים משלהם. מודולים ב-boot classpath שמשתמשים ב-HIDL חייבים להשתמש בגרסאות השטוחות של ספריות ה-Java האלה, ולהוסיף את jarjar_rules: ":framework-jarjar-rules"
ל-Android.bp
שלהם כדי להשתמש בגרסה של הספריות האלה שקיימת ב-boot classpath.
שינוי של קוד המקור ב-Java
יש רק גרסה אחת (@1.0
) של השירות הזה, ולכן הקוד הזה מאחזר רק את הגרסה הזו. במאמר תוספים לממשק מוסבר איך לטפל בכמה גרסאות שונות של השירות.
import android.hardware.foo.V1_0.IFoo; ... // retry to wait until the service starts up if it is in the manifest IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IFoo anotherServer = IFoo.getService("second_impl", true /* retry */); server.doSomething(…);
לספק שירות
יכול להיות שקוד המסגרת ב-Java יצטרך להציג ממשקים כדי לקבל קריאות חזרה (callbacks) אסינכררוניות מ-HALs.
בממשק IFooCallback
בגרסה 1.0 של החבילה android.hardware.foo
, אפשר להטמיע את הממשק ב-Java לפי השלבים הבאים:
- מגדירים את הממשק ב-HIDL.
- פותחים את
/tmp/android/hardware/foo/IFooCallback.java
כחומר עזר. - יוצרים מודול חדש להטמעת Java.
- בודקים את המחלקה המופשטת
android.hardware.foo.V1_0.IFooCallback.Stub
, ואז כותבים מחלקה חדשה כדי להרחיב אותה ולהטמיע את השיטות המופשטות.
הצגת קבצים שנוצרו באופן אוטומטי
כדי להציג את הקבצים שנוצרו באופן אוטומטי, מריצים את הפקודה:
hidl-gen -o /tmp -Ljava \ -randroid.hardware:hardware/interfaces \ -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0
הפקודות האלה יוצרות את הספרייה /tmp/android/hardware/foo/1.0
. עבור הקובץ hardware/interfaces/foo/1.0/IFooCallback.hal
, הפעולה הזו יוצרת את הקובץ /tmp/android/hardware/foo/1.0/IFooCallback.java
, שמכיל את הממשק של Java, את קוד ה-proxy ואת ה-stubs (גם ה-proxy וגם ה-stubs תואמים לממשק).
-Lmakefile
יוצר את הכללים להפעלת הפקודה הזו בזמן ה-build, ומאפשר לכם לכלול את android.hardware.foo-V1.0-java
ולקשר אל הקבצים המתאימים. ב-hardware/interfaces/update-makefiles.sh
אפשר למצוא סקריפט שמבצע את הפעולה הזו באופן אוטומטי בפרויקט מלא בממשקים.
הנתיבים בדוגמה הזו הם יחסיים. אפשר ליצור ספרייה זמנית בשם 'חומרה/ממשקים' מתחת לעץ הקוד כדי לפתח HAL לפני הפרסום שלו.
הרצת שירות
ה-HAL מספק את הממשק IFoo
, שצריך לבצע קריאות חזרה אסינכררוניות למסגרת דרך הממשק IFooCallback
. הממשק IFooCallback
לא רשום לפי שם כשירות שגלוי לכולם. במקום זאת, IFoo
חייב להכיל שיטה כמו setFooCallback(IFooCallback x)
.
כדי להגדיר את IFooCallback
מגרסה 1.0 של חבילת android.hardware.foo
, מוסיפים את android.hardware.foo-V1.0-java
אל Android.mk
. הקוד להפעלת השירות הוא:
import android.hardware.foo.V1_0.IFoo; import android.hardware.foo.V1_0.IFooCallback.Stub; .... class FooCallback extends IFooCallback.Stub { // implement methods } .... // Get the service from which you will be receiving callbacks. // This also starts the threadpool for your callback service. IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available .... // This must be a persistent instance variable, not local, // to avoid premature garbage collection. FooCallback mFooCallback = new FooCallback(); .... // Do this once to create the callback service and tell the "foo-bar" service server.setFooCallback(mFooCallback);
תוספים לממשק
בהנחה ששירות מסוים מטמיע את ממשק IFoo
בכל המכשירים, יכול להיות שבמכשיר מסוים השירות יספק יכולות נוספות שמוטמעות בתוסף הממשק IBetterFoo
, באופן הבא:
interface IFoo { ... }; interface IBetterFoo extends IFoo { ... };
קוד קורא שמודע לממשק המורחב יכול להשתמש בשיטת Java castFrom()
כדי לבצע הטמעה (cast) בטוחה של הממשק הבסיסי לממשק המורחב:
IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IBetterFoo extendedService = IBetterFoo.castFrom(baseService); if (extendedService != null) { // The service implements the extended interface. } else { // The service implements only the base interface. }