הטמעת ספריית Java SDK

פלטפורמת Android מכילה מספר גדול של ספריות Java משותפות שאפשר לכלול ב-classpath של אפליקציות באמצעות התג <uses-library> במניפסט של האפליקציה. אפליקציות מקשרות לספריות האלה, לכן צריך להתייחס אליהן כמו לשאר Android API מבחינת תאימות, בדיקת API ותמיכה בכלים. עם זאת, חשוב לזכור שרוב הספריות לא כוללות את התכונות האלה.

סוג המודול java_sdk_library עוזר לנהל ספריות מהסוג הזה. יצרני המכשירים יכולים להשתמש במנגנון הזה בספריות Java המשותפות שלהם, כדי לשמור על תאימות לאחור לממשקי ה-API שלהם. אם יצרני המכשירים משתמשים בספריות Java משותפות משלהם דרך התג <uses-library> במקום נתיב bootclass, אפשר להשתמש ב-java_sdk_library כדי לוודא שספריות ה-Java האלה יציבות מבחינת ה-API.

ב-java_sdk_library מוטמעים ממשקי API אופציונליים של SDK לשימוש באפליקציות. ספריות שמוטמעות באמצעות java_sdk_library בקובץ ה-build (Android.bp) מבצעות את הפעולות הבאות:

  • ספריות הסטאבים נוצרות כך שיכללו את stubs,‏ stubs.system ו-stubs.test. ספריות ה-stubs נוצרות על ידי זיהוי ההערות @hide,‏ @SystemApi ו-@TestApi.
  • קובצי המפרט של ה-API (כמו current.txt) מנוהלים על ידי java_sdk_library בתיקיית משנה של ה-API. הקבצים האלה נבדקים מול הקוד העדכני ביותר כדי לוודא שהם בגרסה העדכנית ביותר. אם לא, תופיע הודעת שגיאה עם הסבר איך לעדכן אותם. בודקים באופן ידני את כל השינויים שבוצעו בעדכון כדי לוודא שהם תואמים לציפיות.

    כדי לעדכן את כל ממשקי ה-API, משתמשים ב-m update-api. כדי לוודא ש-API עדכני, משתמשים ב-m checkapi.
  • קובצי המפרט של ה-API נבדקים לגבי גרסאות Android שפורסמו לאחרונה, כדי לוודא שה-API תואם לאחור לגרסאות קודמות. המודולים של java_sdk_library שסופקו כחלק מ-AOSP ממוקמים ב-prebuilts/sdk/<latest number>.
  • בנוגע לבדיקות של קובצי מפרטי ה-API, תוכלו לבצע אחת מהשלוש הפעולות הבאות:
    • מאפשרים את המשך הבדיקות. (לא עושים כלום).
    • כדי להשבית את הבדיקות, מוסיפים את הקטע הבא לקובץ java_sdk_library:
      unsafe_ignore_missing_latest_api: true,
    • כדי לספק ממשקי API ריקים למודולים חדשים של java_sdk_library, יוצרים קובצי טקסט ריקים בשם module_name.txt בספרייה version/scope/api.
  • אם ספריית ההטמעה של סביבת זמן הריצה מותקנת, נוצר קובץ XML ומתבצעת התקנה שלו.

איך פועלת java_sdk_library

java_sdk_library שנקרא X יוצר את הפריטים הבאים:

  1. שני עותקים של ספריית ההטמעה: ספרייה אחת שנקראת X וספרייה אחרת שנקראת X.impl. ספריית X מותקנת במכשיר. הספרייה X.impl נמצאת רק אם מודולים אחרים זקוקים לגישה מפורשת לספריית ההטמעה, למשל לצורך בדיקה. חשוב לזכור שנדיר מאוד שנדרשת גישה מפורשת.
  2. אפשר להפעיל ולהשבית היקפים כדי להתאים אישית את הגישה. (בדומה למגבילי הגישה של מילות מפתח ב-Java, היקף גלוי לכולם מספק מגוון רחב של הרשאות גישה, והיקף בדיקה מכיל ממשקי API שמשמשים רק לצורכי בדיקה). לכל היקף מופעל, הספרייה יוצרת את הפריטים הבאים:
    • מודול מקור של stubs (מסוג מודול droidstubs) – צורך את מקור ההטמעה ומפיק קבוצה של מקורות stubs יחד עם קובץ המפרט של ה-API.
    • ספריית stubs (מסוג מודול java_library) – זוהי הגרסה המתומצתת של ה-stubs. הספריות ששימשו להדרכה הזו הן לא אותן ספריות שסופקו ל-java_sdk_library, וכך מוודאים שפרטי ההטמעה לא דולפים לסטאבים של ה-API.
    • אם אתם זקוקים לספריות נוספות כדי לקמפל את הסטאבים, תוכלו להשתמש במאפיינים stub_only_libs ו-stub_only_static_libs כדי לספק אותן.

אם java_sdk_library נקרא X, והוא עובר הידור כ-X, תמיד צריך להתייחס אליו כך ולא לשנות אותו. ה-build יבחר ספרייה מתאימה. כדי לוודא שיש לכם את הספרייה המתאימה ביותר, בודקים את הסטאבים כדי לראות אם ה-build כלל שגיאות. מבצעים את התיקונים הנדרשים לפי ההנחיות הבאות:

  • כדי לוודא שיש לכם ספרייה מתאימה, בודקים את שורת הפקודה ומעיינים בסטאבים שמפורטים בה כדי לקבוע את ההיקף:
    • ההיקף רחב מדי: לספרייה התלויה נדרש היקף מסוים של ממשקי API. עם זאת, בספרייה יש ממשקי API שאינם נכללים בהיקף הזה, כמו ממשקי API של מערכת שכלולים בממשקי ה-API הציבוריים.
    • ההיקף מצומצם מדי: לספרייה התלויה אין גישה לכל הספריות הנדרשות. לדוגמה, הספרייה התלויה צריכה להשתמש ב-API המערכת, אבל במקום זאת היא מקבלת את ה-API הציבורי. בדרך כלל התוצאה היא שגיאת הידור (compilation) כי ממשקי ה-API הנדרשים חסרים.
  • כדי לתקן את הספרייה, מבצעים רק פעולה אחת מהפעולות הבאות:
    • משנים את sdk_version כדי לבחור את הגרסה הרצויה. או
    • מציינים במפורש את הספרייה המתאימה, למשל <X>.stubs או <X>.stubs.system.

שימוש ב-java_sdk_library X

ספריית ההטמעה X נקראת כשיש הפניה אליה מ-apex.java_libs. עם זאת, בגלל מגבלה של Soong, כשמתבצעת הפניה לספרייה X מתוך מודול java_sdk_library אחר באותה ספריית APEX, צריך להשתמש ב-X.impl באופן מפורש ולא בספרייה X.

כשמתבצעת הפניה ל-java_sdk_library ממקור אחר, נעשה שימוש בספריית stubs. ספריית ה-stubs נבחרת בהתאם להגדרת המאפיין sdk_version של המודול התלוי. לדוגמה, מודול שמציין את הערך sdk_version: "current" משתמש ב-stubs הציבוריים, ואילו מודול שמציין את הערך sdk_version: "system_current" משתמש ב-stubs המערכתיים. אם לא ניתן למצוא התאמה מדויקת, המערכת משתמשת בספריית ה-stub הקרובה ביותר. java_sdk_library שמספק רק ממשק API ציבורי יספק את ה-stubs הציבוריים לכולם.

תהליך פיתוח באמצעות ספריית Java SDK
איור 1. תהליך פיתוח באמצעות ספריית Java SDK

דוגמאות ומקורות

המאפיינים srcs ו-api_packages חייבים להופיע ב-java_sdk_library.

java_sdk_library {
        name: "com.android.future.usb.accessory",
        srcs: ["src/**/*.java"],
        api_packages: ["com.android.future.usb"],
    }

ב-AOSP מומלץ (אבל לא נדרש) להפעיל במפורש את היקפי ה-API שבהם רוצים להשתמש במכונות java_sdk_library חדשות. אפשר גם (אופציונלי) להעביר מכונות java_sdk_library קיימות כדי להפעיל באופן מפורש את היקפי ה-API שבהם הן ישתמשו:

java_sdk_library {
         name: "lib",
         public: {
           enabled: true,
         },
         system: {
           enabled: true,
         },
         
    }

כדי להגדיר את ספריית impl שתשמש בסביבת זמן הריצה, משתמשים בכל המאפיינים הרגילים של java_library, כמו hostdex,‏ compile_dex ו-errorprone.

java_sdk_library {
        name: "android.test.base",

        srcs: ["src/**/*.java"],

        errorprone: {
          javacflags: ["-Xep:DepAnn:ERROR"],
        },

        hostdex: true,

        api_packages: [
            "android.test",
            "android.test.suitebuilder.annotation",
            "com.android.internal.util",
            "junit.framework",
        ],

        compile_dex: true,
    }

כדי להגדיר ספריות stubs, משתמשים במאפיינים הבאים:

  • merge_annotations_dirs וגם merge_inclusion_annotations_dirs
  • api_srcs: רשימת קובצי המקור האופציונליים שהם חלק מה-API אבל לא חלק מספריית זמן הריצה.
  • stubs_only_libs: רשימת ספריות Java שנמצאות ב-classpath בזמן היצירה של stubs.
  • hidden_api_packages: רשימת שמות החבילות שצריך להסתיר מה-API.
  • droiddoc_options: ארגומנט נוסף ל-metalava.
  • droiddoc_option_files: רשימה של הקבצים שאפשר להפנות אליהם מתוך droiddoc_options באמצעות $(location <label>), כאשר <file> הוא רשומה ברשימה.
  • annotations_enabled.

java_sdk_library הוא java_library, אבל הוא לא מודול droidstubs ולכן אין לו תמיכה בכל המאפיינים של droidstubs. הדוגמה הבאה נלקחה מהקובץ android.test.mock library build.

java_sdk_library {
        name: "android.test.mock",

        srcs: [":android-test-mock-sources"],
        api_srcs: [
            // Note: The following aren’t APIs of this library. Only APIs under the
            // android.test.mock package are taken. These do provide private APIs
            // to which android.test.mock APIs reference. These classes are present
            // in source code form to access necessary comments that disappear when
            // the classes are compiled into a Jar library.
            ":framework-core-sources-for-test-mock",
            ":framework_native_aidl",
        ],

        libs: [
            "framework",
            "framework-annotations-lib",
            "app-compat-annotations",
            "Unsupportedappusage",
        ],

        api_packages: [
            "android.test.mock",
        ],
        permitted_packages: [
            "android.test.mock",
        ],
        compile_dex: true,
        default_to_stubs: true,
    }

שמירה על תאימות לאחור

מערכת ה-build בודקת אם ממשקי ה-API שמורים על תאימות לאחור, על ידי השוואה בין קובצי ה-API העדכניים לבין קובצי ה-API שנוצרו בזמן ה-build. ה-java_sdk_library מבצע את בדיקת התאימות באמצעות המידע שסופק על ידי prebuilt_apis. כל הספריות שנוצרו באמצעות java_sdk_library חייבות לכלול קובצי API בגרסה האחרונה של api_dirs ב-prebuilt_apis. כשמשחררים את הגרסה, אפשר לקבל את רשימות הקבצים והספריות של ה-stubs של ה-API באמצעות build של dist עם PRODUCT=sdk_phone_armv7-sdk.

המאפיין api_dirs הוא רשימה של ספריות של גרסאות API ב-prebuilt_apis. הספריות של גרסאות ה-API חייבות להיות ברמת הספרייה Android.bp.

prebuilt_apis {
       name: "foo",
       api_dirs: [
           "1",
           "2",
             ....
           "30",
           "current",
       ],
    }

מגדירים את הספריות עם המבנה version/scope/api/ בספריית prebuilts. השדה version תואם לרמת ה-API, והשדה scope מגדיר אם הספרייה היא ציבורית, מערכתית או לבדיקה.

  • version/scope מכילה ספריות Java.
  • version/scope/api מכיל קובצי API‏ .txt. יוצרים כאן קובצי טקסט ריקים בשם module_name.txt ו-module_name-removed.txt.
     ├── 30
             ├── public
                ├── api
                   ├── android.test.mock-removed.txt
                   └── android.test.mock.txt
                └── android.test.mock.jar
             ├── system
                ├── api
                   ├── android.test.mock-removed.txt
                   └── android.test.mock.txt
                └── android.test.mock.jar
             └── test
                 ├── api
                    ├── android.test.mock-removed.txt
                    └── android.test.mock.txt
                 └── android.test.mock.jar
          └── Android.bp