Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

運行時資源覆蓋(RRO)

運行時資源覆蓋(RRO)是在運行時更改目標程序包的資源值的程序包。例如,安裝在系統映像上的應用程序可能會根據資源的值更改其行為。安裝在其他分區上的RRO可以在運行時更改應用程序資源的值,而不是在構建時對資源值進行硬編碼。

可以啟用或禁用RRO。您可以以編程方式設置啟用/禁用狀態,以切換RRO更改資源值的能力。默認情況下禁用RRO(但是,默認情況下啟用靜態RRO )。

疊加資源

覆蓋層通過將覆蓋層包中定義的資源映射到目標包中定義的資源來工作。當應用嘗試解析目標包中資源的值時,將返回目標資源映射到的覆蓋資源的值。

設置清單

如果軟件包包含<overlay>標記作為<manifest>標記的子代,則該軟件包被視為RRO軟件包。

  • 必需的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文件。此外,清單中<application >標記的android:hasCode屬性必須設置為false

定義資源圖

在Android 11或更高版本中,建議的用於定義疊加資源映射的機制是在疊加包的res/xml目錄中創建一個文件,枚舉應疊加的目標資源及其替換值,然後設置<overlay>清單標籤的android:resourcesMap屬性指向對資源映射文件的引用。

以下代碼顯示了一個示例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或更高版本支持覆蓋的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或更高版本中,每個疊加層都有其自己的保留資源ID空間,該空間不與目標資源ID空間或其他疊加層資源ID空間重疊,因此引用其自身資源的疊加層將按預期工作。

  • 在Android 10或更低版本中,疊加層和目標包共享相同的資源ID空間,當它們嘗試使用@type/name語法引用自己的資源時,可能導致衝突和意外行為。

啟用/禁用覆蓋

使用OverlayManager API啟用和禁用可變的疊加層(使用Context#getSystemService(Context.OVERLAY_SERVICE)檢索API接口)。疊加層只能由其定位的包或具有android.permission.CHANGE_OVERLAY_PACKAGES權限的包啟用。啟用或禁用覆蓋時,配置更改事件會傳播到目標軟件包,然後重新啟動目標活動。

限制可疊加資源

在Android 10或更高版本中, <overlayable> XML標籤公開了一組允許<overlayable>覆蓋的資源。在下面的示例res/values/overlayable.xml文件中, string/foointeger/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 。產品分區上的任何覆蓋都可以覆蓋資源。
  • signature 。使用與目標APK相同的簽名簽名的任何疊加層都可以覆蓋資源。

以下代碼顯示了res/values/overlayable.xml文件中的示例<policy>標記。

<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:isStaticandroid:priority )配置靜態RRO。

使用OverlayConfig

在Android 11或更高版本中,您可以使用OverlayConfig來配置疊加層的可變性,默認狀態和優先級。要配置覆蓋,請創建或修改位於partition/overlay/config/config.xml ,其中partition是要配置的覆蓋的分區。要進行配置,覆蓋層必須位於配置該覆蓋overlay/的分區的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
  • oem
  • odm
  • product
  • system_ext

合併文件

使用<merge>標記允許將其他配置文件在指定位置合併到配置文件中。標記的path屬性表示要合併的文件相對於包含覆蓋配置文件的目錄的路徑。

使用清單屬性(靜態RRO)

在Android 10或更低版本中,疊加層不變性和優先級使用以下清單屬性進行配置。

  • android:isStatic 。當此boolean屬性的值設置為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:isStaticandroid:priority不會影響分區中的疊加層。在任何分區中定義覆蓋配置文件將強制覆蓋分區優先級。

此外,Android 11或更高版本取消了使用靜態覆蓋來影響軟件包安裝期間讀取的資源值的功能。對於使用靜態疊加層來更改配置組件啟用狀態的布爾值的常見用例,請使用<component-override> SystemConfig標籤(Android 11中的新增功能)。

調試疊加

要手動啟用,禁用和轉儲疊加層,請使用以下overlay manager shell命令。

adb shell cmd overlay

OverlayManagerService使用idmap2將目標程序包中的資源ID映射到覆蓋程序包中的資源ID。生成的ID映射存儲在/data/resource-cache/ 。如果您的疊加層無法正常工作,請在/data/resource-cache/找到與您的疊加層相對應的idmap文件,然後運行以下命令。

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