運行時資源覆蓋 (RRO)

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

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

疊加資源

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

設置清單

如果它包含一個A包被認為是一個包RRO <overlay>標記為的子<manifest>標記。

  • 所需的值android:targetPackage屬性指定包的名稱RRO打算覆蓋。

  • 可選的價值android:targetName屬性指定目標包的RRO擬覆蓋的資源overlayable子集的名稱。如果目標未定義可覆蓋的資源集,則不應存在此屬性。

下面的代碼顯示疊加的一例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>

Overlays 不能覆蓋代碼,所以它們不能有 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>

構建包

機器人11或更高的載體用於宋生成規則覆蓋,防止機器人資產打包工具2從試圖資源的重複數據刪除的配置具有相同的值((AAPT2) --no-resource-deduping ),並從沒有默認配置中除去資源( --no-resource-removal )。下面的代碼示出了示例Android.bp文件。

runtime_resource_overlay {
    name: "ExampleOverlay",
    sdk_version: "current",
}

解析資源

如果目標資源或覆蓋資源為被查詢的資源定義了多個配置,則資源運行時會返回與設備配置的配置最匹配的配置值。要確定哪些配置是最佳匹配的配置,合併組覆蓋資源配置到該組目標資源的配置,然後按照常規的資源解析流(有關詳細信息,請參閱如何安卓找到最匹配的資源)。

例如,如果覆蓋定義了的值drawable-en配置和目標定義的值drawable-en-portdrawable-en-port具有更好的匹配,從而目標配置的值drawable-en-port是在運行時選擇。疊加所有drawable-en配置中,覆蓋必須為每個的值drawable-en構型的目標定義。

Overlays 可以引用它們自己的資源,在 Android 版本之間具有不同的行為。

  • 在 Android 11 或更高版本中,每個疊加層都有自己的預留資源 ID 空間,不會與目標資源 ID 空間或其他疊加層資源 ID 空間重疊,因此疊加層引用自己的資源按預期工作。

  • 在Android的10或更低,覆蓋和目標包共享相同的資源ID空間,這可能會導致衝突和意外的行為,當他們嘗試使用引用自己的資源@type/name語法。

啟用/禁用覆蓋

使用OverlayManager API啟用和禁用可變覆蓋(檢索API接口使用Context#getSystemService(Context.OVERLAY_SERVICE)一個覆蓋只能通過它的目標的包或與包被啟用android.permission.CHANGE_OVERLAY_PACKAGES許可。啟用或禁用覆蓋時,配置更改事件會傳播到目標包並重新啟動目標活動。

限制可覆蓋資源

在Android中10或更高時, <overlayable> XML標籤暴露了複製權組織被允許重疊的一組資源。在以下示例中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>名稱。

您不能啟用覆蓋目標暴露overlayable資源,但不使用包裝android:targetName ,指定特定<overlayable>標籤。

限制政策

使用<policy>標籤來實施對overlayable資源的限制。該type屬性指定哪些政策疊加必須履行覆蓋包含的資源。支持的類型包括以下。

  • public 。任何覆蓋都可以覆蓋資源。
  • system 。系統分區上的任何覆蓋都可以覆蓋資源。
  • vendor 。供應商分區上的任何覆蓋都可以覆蓋資源。
  • product 。產品分區上的任何覆蓋都可以覆蓋資源。
  • signature 。使用與目標 APK 相同的簽名簽名的任何覆蓋都可以覆蓋資源。

下面的代碼示出了示例<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:isStaticandroid:priority )配置靜態複製權組織。

使用 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
  • oem
  • odm
  • 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:isStaticandroid:priority不必在位於隔板覆蓋的效果。在任何分區中定義覆蓋配置文件都會強制執行覆蓋分區優先級。

此外,Android 11 或更高版本移除了使用靜態覆蓋來影響包安裝期間讀取的資源值的功能。對於使用靜態疊加來改變配置組件啟用狀態布爾值的值的共同使用的情況下,可使用<component-override> SystemConfig標籤(Android中11個新的)。

調試覆蓋

要手動啟用、禁用和轉儲覆蓋,請使用以下覆蓋管理器 shell 命令。

adb shell cmd overlay

OverlayManagerService用途idmap2在上層包中的目標包資源ID映射到資源ID。所生成的ID映射存儲在/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