שינוי הערך של משאבי האפליקציה בזמן הריצה

שכבת-על של משאבים בסביבת זמן ריצה (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 בקובץ system.config.
  • config_signature. כל שכבת-על חתומה באותה חתימה כמו קובץ ה-APK של 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 לא משפיעים על שכבות-העל שנמצאות במחיצה. הגדרת קובץ תצורה של שכבת-על בכל מחיצה אוכפת את העדיפות של מחיצה שכבת-העל.

בנוסף, מגרסה 11 ואילך של Android, לא ניתן להשתמש בשכבות-על סטטיות כדי להשפיע על הערכים של המשאבים שנקראים במהלך התקנת החבילה. בתרחיש לדוגמה נפוץ של שימוש בשכבות-על סטטיות כדי לשנות את הערך של ערכים בוליאניים שמגדירים את מצב ההפעלה של הרכיב, משתמשים בתג <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