שכבת-על של משאבים בסביבת זמן ריצה (RRO) היא חבילה שמשנה את ערכי המשאבים של חבילת יעד בסביבת זמן ריצה. לדוגמה, אפליקציה שמותקנת בתמונת המערכת עשויה לשנות את ההתנהגות שלה על סמך הערך של משאב. במקום להטמיע את ערך המשאב בזמן ה-build, קובץ RRO שמותקן במחיצה אחרת יכול לשנות את הערכים של משאבי האפליקציה בזמן הריצה.
אפשר להפעיל או להשבית את ה-RRO. אפשר להגדיר באופן פרוגרמטי את המצב מופעל/מושבת כדי להחליף את היכולת של RRO לשנות את ערכי המשאבים. קובצי RRO מושבתים כברירת מחדל (עם זאת, קובצי RRO סטטיים מופעלים כברירת מחדל).
משאבי שכבת-על
שכבות-על פועלות על ידי מיפוי משאבים שמוגדרים בחבילת שכבת-העל למשאבים שמוגדרים בחבילת היעד. כשאפליקציה מנסה לפתור את הערך של משאב בחבילת היעד, במקום זאת מוחזר הערך של משאב שכבת-העל שאליו משאב היעד ממופה.
הגדרת המניפסט
חבילה נחשבת לחבילת RRO אם היא מכילה תג <overlay>
בתור הצאצא של התג <manifest>
.
הערך של המאפיין הנדרש
android:targetPackage
מציין את שם החבילה שה-RRO מתכוון להוסיף.הערך של המאפיין האופציונלי
android:targetName
מציין את השם של קבוצת המשנה שניתנת לשכבת-על של המשאבים של חבילת היעד שה-RRO מתכוון ליצור שכבת-על. אם היעד לא מגדיר קבוצה של משאבים שאפשר להציג שכבה-על שלהם, המאפיין הזה לא אמור להופיע.
הקוד הבא מציג שכבת-על לדוגמה AndroidManifest.xml
.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.overlay">
<application android:hasCode="false" />
<overlay android:targetPackage="com.example.target"
android:targetName="OverlayableResources"/>
</manifest>
לא ניתן להוסיף שכבות-על לקוד, ולכן הן לא יכולות לכלול קובצי DEX. בנוסף, צריך להגדיר את המאפיין android:hasCode
של התג <application
> במניפסט לערך false
.
הגדרת מפת המשאבים
ב-Android 11 ואילך, המנגנון המומלץ להגדרת המיפוי של משאבי שכבת-העל הוא ליצור קובץ בתיקייה res/xml
של חבילת שכבת-העל, למנות את משאבי היעד שצריך להוסיף שכבת-על ואת ערכי ההחלפה שלהם, ואז להגדיר את הערך של המאפיין android:resourcesMap
של תג המניפסט <overlay>
כהפניה לקובץ מיפוי המשאבים.
הקוד הבא מציג קובץ res/xml/overlays.xml
לדוגמה.
<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Overlays string/config1 and string/config2 with the same resource. -->
<item target="string/config1" value="@string/overlay1" />
<item target="string/config2" value="@string/overlay1" />
<!-- Overlays string/config3 with the string "yes". -->
<item target="string/config3" value="@android:string/yes" />
<!-- Overlays string/config4 with the string "Hardcoded string". -->
<item target="string/config4" value="Hardcoded string" />
<!-- Overlays integer/config5 with the integer "42". -->
<item target="integer/config5" value="42" />
</overlay>
הקוד הבא מציג דוגמה למניפסט של שכבת-על.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.overlay">
<application android:hasCode="false" />
<overlay android:targetPackage="com.example.target"
android:targetName="OverlayableResources"
android:resourcesMap="@xml/overlays"/>
</manifest>
יצירת החבילה
ב-Android מגרסה 11 ואילך יש תמיכה בכלל build של Soong לשכבות-על, שמנע מ-Android Asset Packaging Tool 2 (AAPT2) לנסות לבטל כפילויות של הגדרות של משאבים עם אותו ערך (--no-resource-deduping
) ולהסיר משאבים ללא הגדרות ברירת מחדל (--no-resource-removal
). הקוד הבא מציג קובץ Android.bp
לדוגמה.
runtime_resource_overlay {
name: "ExampleOverlay",
sdk_version: "current",
}
טיפול במשאבים
אם למשאב יעד או למשאב שכבת-על מוגדרות כמה תצורות למשאב שאליו מתבצעת השאילתה, סביבת זמן הריצה של המשאבים מחזירה את ערך התצורה שתואמת בצורה הטובה ביותר לתצורה של הגדרות המכשיר. כדי לקבוע איזו הגדרה היא ההתאמה הטובה ביותר, ממזגים את הקבוצה של הגדרות המשאב של שכבת-העל עם הקבוצה של הגדרות המשאב היעד, ולאחר מכן פועלים לפי תהליך פתרון המשאב הרגיל (פרטים נוספים זמינים במאמר איך Android מאתרת את המשאב הכי תואם).
לדוגמה, אם שכבת-על מגדירה ערך להגדרה drawable-en
והיעד מגדיר ערך ל-drawable-en-port
, ל-drawable-en-port
יש התאמה טובה יותר ולכן הערך של הגדרת היעד drawable-en-port
נבחר בזמן הריצה. כדי להוסיף שכבת-על לכל הגדרות drawable-en
, שכבת-העל חייבת להגדיר ערך לכל הגדרת drawable-en
שהיעד מגדיר.
שכבות-על יכולות להפנות למשאבים משלהם, עם התנהגויות שונות בין גרסאות Android.
ב-Android 11 ואילך, לכל שכבת-על יש מרחב משלו שמוגדר למזהי משאבים, שלא חופף למרחב של מזהי המשאבים של היעד או למרחבים אחרים של מזהי משאבים של שכבות-על. לכן, שכבות-על שמתייחסות למשאבים שלהן פועלות כצפוי.
ב-Android מגרסה 10 ואילך, לחבילות שכבת-העל ולחבילות היעד יש את אותו מרחב מזהי משאבים, מה שעלול לגרום להתנגשויות ולהתנהגות בלתי צפויה כשהן מנסים להפנות למשאבים שלהן באמצעות התחביר
@type/name
.
הפעלה/השבתה של שכבות-על
משתמשים ב-API OverlayManager
כדי להפעיל ולהשבית שכבות-על שניתן לשנות (אחזור ממשק ה-API באמצעות Context#getSystemService(Context.OVERLAY_SERVICE)
). אפשר להפעיל שכבת-על רק באמצעות החבילה שהיא מטרגטת או באמצעות חבילה עם ההרשאה android.permission.CHANGE_OVERLAY_PACKAGES
. כששכבת-על מופעלת או מושבתת, אירועי שינוי בתצורה מועברים לחבילת היעד והפעילויות של היעד מופעלות מחדש.
הגבלת משאבים שאפשר להוסיף שכבה עליהם
ב-Android מגרסה 10 ואילך, תג ה-XML מסוג <overlayable>
חושף קבוצת משאבים שמותר לקובצי RRO להציג כשכבת-על. בקובץ res/values/overlayable.xml
לדוגמה, string/foo
ו-integer/bar
הם משאבים המשמשים לעיצוב המראה של המכשיר. כדי להציג שכבת-על על המשאבים האלה, שכבת-העל צריכה לטרגט באופן מפורש את אוסף המשאבים שאפשר להציג עליהם שכבת-על לפי שם.
<!-- The collection of resources for theming the appearance of the device -->
<overlayable name="ThemeResources">
<policy type="public">
<item type="string" name="foo/" />
<item type="integer" name="bar/" />
</policy>
...
</overlayable>
קובץ APK יכול להגדיר כמה תגי <overlayable>
, אבל לכל תג צריך להיות שם ייחודי בחבילה. לדוגמה:
אוקיי, שתי חבילות שונות מגדירות את
<overlayable name="foo">
.לא מתאים ל-APK יחיד לכלול שתי חסימות של
<overlayable name="foo">
.
הקוד הבא מציג דוגמה לשכבת-על בקובץ AndroidManifest.xml
.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.my.theme.overlay">
<application android:hasCode="false" />
<!-- This overlay will override the ThemeResources resources -->
<overlay android:targetPackage="android" android:targetName="ThemeResources">
</manifest>
כשאפליקציה מגדירה תג <overlayable>
, שכבות-על שמטרגטות את האפליקציה הזו:
חובה לציין את
targetName
.אפשר להציג שכבת-על רק למשאבים שמפורטים בתג
<overlayable>
.אפשר לטרגט רק שם
<overlayable>
אחד.
אי אפשר להפעיל שכבת-על שמטרגטת חבילה שחושפת משאבים שניתנים בשכבת-על אבל לא משתמשת ב-android:targetName
כדי לטרגט תג <overlayable>
ספציפי.
כללי מדיניות
משתמשים בתג <policy>
כדי לאכוף הגבלות על משאבים שאפשר להציג מעליהם שכבות-על. המאפיין type
מציין אילו כללי מדיניות שכבת-העל צריכה לעמוד בהם כדי לשנות את המשאבים הכלולים. סוגי הקבצים הנתמכים הם:
public
. כל שכבת-על יכולה לשנות את המשאב.system
. כל שכבת-על במחיצה של המערכת יכולה לשנות את המשאבים.vendor
. כל שכבת-על במחיצה של הספק יכולה לשנות את המשאבים.product
. כל שכבת-על במחיצה של המוצר יכולה לשנות את המשאבים.oem
. כל שכבת-על במחיצה של OEM יכולה לשנות את המשאבים.odm
. כל שכבת-על במחיצה של ODM יכולה לשנות את המשאבים.signature
. כל שכבת-על חתומה באותה חתימה כמו חבילת ה-APK היעד יכולה לשנות את המשאבים.actor
. כל שכבת-על חתומה באותה חתימה כמו חבילת ה-APK של הגורם יכולה לשנות את המשאבים. המשתמש מוצהר בתג named-actor בתצורת המערכת.config_signature
. כל שכבת-על חתומה באותה חתימה כמו קובץ ה-APK של overlay-config יכולה לשנות את המשאבים. ההגדרה של overlay-config מוצהרת בתג overlay-config-signature בהגדרות המערכת.
הקוד הבא מציג תג <policy>
לדוגמה בקובץ res/values/overlayable.xml
.
<overlayable name="ThemeResources">
<policy type="vendor" >
<item type="string" name="foo" />
</policy>
<policy type="product|signature" >
<item type="string" name="bar" />
<item type="string" name="baz" />
</policy>
</overlayable>
כדי לציין כמה כללי מדיניות, משתמשים בקו אנכי (|) כתו מפריד.
כשמציינים כמה כללי מדיניות, שכבת-על צריכה לעמוד רק במדיניות אחת כדי לשנות את המשאבים שמפורטים בתג <policy>
.
הגדרת שכבות-על
מערכת Android תומכת במנגנונים שונים להגדרת היכולת לשינוי, מצב ברירת המחדל והעדיפות של שכבות-על, בהתאם לגרסה של Android.
במכשירים עם Android מגרסה 11 ואילך אפשר להשתמש בקובץ
OverlayConfig
(config.xml
) במקום במאפייני מניפסט. השיטה המומלצת לשימוש בשכבות-על היא שימוש בקובץ שכבת-על.כל המכשירים יכולים להשתמש במאפייני מניפסט (
android:isStatic
ו-android:priority
) כדי להגדיר ערכי RRO סטטיים.
שימוש ב-OverlayConfig
ב-Android מגרסה 11 ואילך, אפשר להשתמש ב-OverlayConfig
כדי להגדיר את יכולת השינוי, את מצב ברירת המחדל והעדיפות של שכבות-על. כדי להגדיר שכבת-על, יוצרים או משנים את הקובץ שנמצא ב-partition/overlay/config/config.xml
, כאשר partition
הוא המחיצה של שכבת-העל שרוצים להגדיר. כדי להגדיר שכבת-על, חייבת להיות בספרייה overlay/
של המחיצה שבה שכבת-העל מוגדרת. הקוד הבא מציג דוגמה ל-product/overlay/config/config.xml
.
<config>
<merge path="OEM-common-rros-config.xml" />
<overlay package="com.oem.overlay.device" mutable="false" enabled="true" />
<overlay package="com.oem.green.theme" enabled="true" />
</config>"
התג <overlay>
מחייב מאפיין package
שמציין איזו חבילת שכבת-על מוגדרת. המאפיין האופציונלי enabled
קובע אם שכבת-העל מופעלת כברירת מחדל (ברירת המחדל היא false
). המאפיין האופציונלי mutable
קובע אם שכבת-העל ניתנת לשינוי, ואם אפשר לשנות את מצב ההפעלה שלה באופן פרוגרמטי בזמן הריצה (ברירת המחדל היא true
). שכבות-על שלא מופיעות בקובץ תצורה ניתנות לשינוי ומושבתות כברירת מחדל.
קדימות של שכבת-על
כשיש כמה שכבות-על שמחליפות את אותם משאבים, הסדר של השכבות-העל חשוב. לשכבת-על יש קדימות גדולה יותר מאשר לשכבות-על עם הגדרות שקודמות להגדרה שלה. סדר הקדימות של שכבות-על במחיצות שונות (מהקדימות הנמוכה ביותר לקדימות הגבוהה ביותר) הוא:
system
vendor
odm
oem
product
system_ext
מיזוג קבצים
שימוש בתגי <merge>
מאפשר למזג קובצי תצורה אחרים במיקום שצוין בקובץ התצורה. המאפיין path
של התג מייצג את הנתיב של הקובץ למיזוג ביחס לספרייה שמכילה את קובצי התצורה של שכבת-העל.
שימוש במאפייני מניפסט או ב-RRO סטטיים
ב-Android 10 וגרסאות ישנות יותר, העדיפות והאי-אפשרות לשינוי של שכבת-העל מוגדרות באמצעות מאפייני המניפסט הבאים.
android:isStatic
. כשהערך של המאפיין הבוליאני הזה מוגדר ל-true
, שכבת-העל מופעלת כברירת מחדל ואי אפשר לשנות אותה, כך שלא ניתן להשבית אותה.android:priority
. הערך של המאפיין המספרי הזה (שמשפיע רק על שכבות-על סטטיות) קובע את תעדוף השכבה-העל כשיש כמה שכבות-על סטטיות שמטרגטות את אותו ערך משאב. ככל שהמספר גבוה יותר, כך העדיפות גבוהה יותר.
הקוד הבא מציג AndroidManifest.xml
לדוגמה.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.overlay">
<application android:hasCode="false" />
<overlay android:targetPackage="com.example.target"
android:isStatic="true"
android:priority="5"/>
</manifest>
שינויים ב-Android 11
ב-Android 11 ואילך, אם קובץ תצורה נמצא ב-partition/overlay/config/config.xml
, שכבות-העל מוגדרות באמצעות הקובץ הזה, ו-android:isStatic
ו-android:priority
לא משפיעים על שכבות-העל שנמצאות במחיצה. הגדרת קובץ תצורה של שכבת-על במחיצה כלשהי אוכפת את הקדימות של מחיצת שכבת-העל.
בנוסף, בגרסה Android 11 ואילך אין אפשרות להשתמש בשכבות-על סטטיות כדי להשפיע על הערכים של המשאבים שנקראו במהלך התקנת החבילה. בתרחיש לדוגמה נפוץ של שימוש בשכבות-על סטטיות כדי לשנות את הערך של ערכים בוליאניים שמגדירים את מצב ההפעלה של הרכיב, משתמשים בתג <component-override>
SystemConfig
(חדש ב-Android 11).
שכבות-על של ניפוי באגים
כדי להפעיל, להשבית ולשמור ידנית שכבות-על, משתמשים בפקודה הבאה של מעטפת מנהל שכבות-העל.
adb shell cmd overlay
OverlayManagerService
משתמש ב-idmap2
כדי למפות את מזהי המשאבים בחבילת היעד למזהי המשאבים בחבילת שכבת-העל. מיפויי המזהים שנוצרו נשמרים ב-/data/resource-cache/
. אם שכבת-העל לא פועלת כמו שצריך, מחפשים את קובץ ה-idmap
התואם לשכבת-העל ב-/data/resource-cache/
ומריצים את הפקודה הבאה.
adb shell idmap2 dump --idmap-path [file]
הפקודה הזו מדפיסה את המיפוי של המשאבים, כפי שמוצג בהמשך.
[target res id] - > [overlay res id] [resource name]
0x01040151 -> 0x01050001 string/config_dozeComponent
0x01040152 -> 0x01050002 string/config_dozeDoubleTapSensorType
0x01040153 -> 0x01050003 string/config_dozeLongPressSensorType