פלטפורמת Android מכילה קובצי XML רבים לאחסון נתוני הגדרות (לדוגמה, הגדרות אודיו). רבים מקובצי ה-XML נמצאים במחיצה vendor
, אבל קוראים אותם במחיצה system
. במקרה כזה, הסכימה של קובץ ה-XML משמשת כממשק בין שתי המחיצות, ולכן צריך לציין את הסכימה באופן מפורש ולהתפתח בשיטה תואמת לאחור.
לפני Android 10, הפלטפורמה לא סיפקה מנגנונים שמחייבים לציין את סכימת ה-XML ולהשתמש בה, או למנוע שינויים לא תואמים בסכימה. מערכת Android 10 מספקת את המנגנון הזה, שנקרא Config File Schema API. המנגנון הזה מורכב מכלי שנקרא xsdc
וכלל build שנקרא xsd_config
.
הכלי xsdc
הוא מהדר של מסמכי סכימת XML (XSD). מנתחת קובץ XSD שמתארת את הסכימה של קובץ XML, ויוצרת קוד Java ו-C++. הקוד שנוצר מנתח קובצי XML שתואמים לסכימת ה-XSD ליצירת עץ של אובייקטים, שכל אחד מהם יוצר תג XML. המודלים של מאפייני XML הם שדות של האובייקטים.
כלל ה-build של xsd_config
משלב את הכלי xsdc
במערכת ה-build.
עבור קובץ קלט נתון XSD, כלל ה-build יוצר ספריות Java ו-C++. אפשר לקשר את הספריות למודולים שבהם קוראים את קובצי ה-XML שתואמים ל-XSD ומשתמשים בהם. תוכלו להשתמש בכלל build לקובצי XML משלכם בשימוש בין המחיצות system
ו-vendor
.
ממשק API של סכימת קבצים ב-Build Config
בקטע הזה נסביר איך לפתח את Config File Schema API.
הגדרת כלל ה-build xsd_config ב-Android.bp
כלל ה-build של xsd_config
יוצר את קוד המנתח באמצעות הכלי xsdc
. המאפיין package_name
של כלל ה-build xsd_config
קובע את שם החבילה של קוד Java שנוצר.
דוגמה לכלל build מסוג xsd_config
ב-Android.bp
:
xsd_config {
name: "hal_manifest",
srcs: ["hal_manifest.xsd"],
package_name: "hal.manifest",
}
דוגמה למבנה ספרייה:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
מערכת ה-build יוצרת רשימת ממשקי API באמצעות קוד ה-Java שנוצר ובודקת את ה-API מולו. בדיקת ה-API הזו נוספה ל-DroidCore ובוצעה ב-m -j
.
יצירת קובצי רשימות של ממשקי API
בדיקות ה-API מחייבות קבצים של רשימות API בקוד המקור.
קובצי הרשימה של ה-API כוללים:
current.txt
ו-removed.txt
בודקים אם ממשקי ה-API משתנים על ידי השוואה בין קובצי ה-API שנוצרו בזמן ה-build.last_current.txt
ו-last_removed.txt
בודקים אם ממשקי ה-API תואמים לאחור על ידי השוואה לקובצי API.
כדי ליצור קבצים של רשימות API:
- ליצור קובצי רשימות ריקים.
- מריצים את הפקודה
make update-api
.
שימוש בקוד הניתוח שנוצר
כדי להשתמש בקוד Java שנוצר, מוסיפים את :
כקידומת לשם המודול xsd_config
במאפיין srcs
של Java. החבילה של קוד Java שנוצר זהה לחבילה package_name
.
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
כדי להשתמש בקוד C++ שנוצר, מוסיפים את שם המודול xsd_config
לנכסים generated_sources
ו-generated_headers
. ומוסיפים את libxml2
ל-static_libs
או ל-shared_libs
, כי נדרש libxml2
בקוד המנתח שנוצר. מרחב השמות של קוד C++ שנוצר זהה לנכס package_name
. לדוגמה, אם שם המודול xsd_config
הוא hal.manifest
, מרחב השמות הוא hal::manifest
.
cc_library{
name: "vintf_test_cpp",
srcs: ["main.cpp"],
generated_sources: ["hal_manifest"],
generated_headers: ["hal_manifest"],
shared_libs: ["libxml2"],
}
שימוש במנתח
כדי להשתמש בקוד של מנתח Java, צריך להשתמש ב-method XmlParser#read
או read{class-name}
כדי להחזיר את המחלקה של רכיב השורש. הניתוח מתבצע בשלב הזה.
import hal.manifest.*;
…
class HalInfo {
public String name;
public String format;
public String optional;
…
}
void readHalManifestFromXml(File file) {
…
try (InputStream str = new BufferedInputStream(new FileInputStream(file))) {
Manifest manifest = XmlParser.read(str);
for (Hal hal : manifest.getHal()) {
HalInfo halinfo;
HalInfo.name = hal.getName();
HalInfo.format = hal.getFormat();
HalInfo.optional = hal.getOptional();
…
}
}
…
}
כדי להשתמש בקוד מנתח C++, תחילה יש לכלול את קובץ הכותרת. השם של קובץ הכותרת הוא שם החבילה שהנקודות (.) מומרות לקווים תחתונים (_).
אחר כך משתמשים ב-method read
או read{class-name}
כדי להחזיר את המחלקה של רכיב הבסיס. הניתוח מתבצע בשלב הזה. הערך המוחזר הוא std::optional<>
.
include "hal_manifest.h"
…
using namespace hal::manifest
struct HalInfo {
public std::string name;
public std::string format;
public std::string optional;
…
};
void readHalManifestFromXml(std::string file_name) {
…
Manifest manifest = *read(file_name.c_str());
for (Hal hal : manifest.getHal()) {
struct HalInfo halinfo;
HalInfo.name = hal.getName();
HalInfo.format = hal.getFormat();
HalInfo.optional = hal.getOptional();
…
}
…
}
כל ממשקי ה-API שסופקו לשימוש במנתח נמצאים ב-api/current.txt
. כדי לשמור על אחידות, כל שמות הרכיבים והמאפיינים מומרים לאותיות קאמליות (לדוגמה, ElementName
) ומשמשים כמשתנה, השיטה ושם המחלקה המתאימים. אפשר לקבל את המחלקה של רכיב הבסיס המנותח באמצעות הפונקציה read{class-name}
. אם יש רק רכיב Root אחד, שם הפונקציה הוא read
. אפשר לקבל את הערך של רכיב משנה או מאפיין שנותח באמצעות הפונקציה get{variable-name}
.
יצירת קוד מנתח
ברוב המקרים, אין צורך להריץ את xsdc
ישירות. במקום זאת, משתמשים בכלל ה-build xsd_config
, כפי שמתואר במאמר הגדרת כלל ה-build xsd_config ב-Android.bp. בקטע הזה מוסבר על ממשק שורת הפקודה xsdc
, רק לצורך שלמותו. האפשרות הזו יכולה לעזור לניפוי באגים.
עליכם לתת לכלי xsdc
את הנתיב לקובץ ה-XSD, וחבילה. החבילה היא שם חבילה בקוד Java ומרחב שמות בקוד C++. האפשרויות כדי לקבוע אם הקוד שנוצר הוא Java או C הן -j
או -c
, בהתאמה. האפשרות -o
היא הנתיב של ספריית הפלט.
usage: xsdc path/to/xsd_file.xsd [-c] [-j] [-o <arg>] [-p]
-c,--cpp Generate C++ code.
-j,--java Generate Java code.
-o,--outDir <arg> Out Directory
-p,--package Package name of the generated java file. file name of
generated C++ file and header
פקודה לדוגמה:
$ xsdc audio_policy_configuration.xsd -p audio.policy -j