פלטפורמת 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.
כלל ה-build יוצר ספריות Java ו-C++ עבור קובץ קלט XSD נתון. אפשר לקשר את הספריות למודולים שבהם קובצי ה-XML שתואמים ל-XSD נקראים ומשמשים. אפשר להשתמש בכלל ה-build בקובצי ה-XML שלכם, שנעשה בהם שימוש במחיצות system
ו-vendor
.
Build Config File Schema API
בקטע הזה מוסבר איך ליצור את 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, משתמשים בשיטה 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++, צריך לכלול קודם את קובץ הכותרת. שם קובץ הכותרת הוא שם החבילה, כאשר הנקודות (.) הופכות לקווים תחתונים (_).
לאחר מכן משתמשים בשיטה 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
. כדי לשמור על אחידות, כל שמות הרכיבים והמאפיינים מומרים ל-camel case (למשל, ElementName
) ומשמשים כשם המשתנה, השיטה והכיתה התואמים. אפשר לקבל את המחלקה של רכיב הבסיס המנותח באמצעות הפונקציה read{class-name}
. אם יש רק רכיב שורש אחד, שם הפונקציה הוא read
. אפשר לקבל את הערך של מאפיין או רכיב משנה שעבר ניתוח באמצעות הפונקציה get{variable-name}
.
יצירת קוד מנתח
ברוב המקרים, אין צורך להריץ את xsdc
ישירות. במקום זאת, צריך להשתמש בכלל ה-build xsd_config
, כפי שמתואר במאמר הגדרת כלל ה-build xsd_config ב-Android.bp. בקטע הזה מוסבר על ממשק שורת הפקודה xsdc
, לצורך השלמות. האפשרות הזו עשויה להיות שימושית לניפוי באגים.
עליכם לתת לכלי xsdc
את הנתיב לקובץ ה-XSD, וחבילה. החבילה היא שם החבילה בקוד 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