在執行階段變更應用程式的值('s)

執行階段資源重疊 (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 資產封裝工具 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 標記會公開一組允許 RRO 疊加的資源。在以下範例 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:產品分區上的任何重疊元素都可以覆寫資源。
  • oem:oem 分區上的任何重疊元素都可以覆寫資源。
  • odm:奇數分區上的任何重疊元素都可以覆寫資源。
  • signature:任何與目標 APK 使用相同簽章簽署的重疊元素都可以覆寫資源。
  • actor:任何與「actor」 APK 具有相同簽章的重疊元素,都可以覆寫資源。系統設定中的 named-actor 標記會宣告演員。
  • config_signature。任何與 overlay-config apk 相同簽章的覆蓋圖層,都可以覆寫資源。系統設定中的 overlay-config-signature 標記會宣告疊加層設定。

以下程式碼為 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/ 目錄中。以下程式碼為 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:isStaticandroid:priority 不會影響位於分割區的覆蓋層。在任何分區中定義重疊設定檔,可強制套用重疊分區優先順序。

此外,Android 11 以上版本無法再使用靜態疊加層,影響安裝套件時讀取的資源值。針對使用靜態覆蓋來變更布建元件啟用狀態的布林值的常見用途,請使用 <component-override> SystemConfig 標記 (Android 11 的新功能)。

偵錯重疊畫面

如要手動啟用、停用和傾印疊加層,請使用下列疊加層管理員殼層指令。

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