Изменение стоимости ресурсов приложения во время выполнения

Наложение ресурсов времени выполнения (RRO) — это пакет, который изменяет значения ресурсов целевого пакета во время выполнения. Например, приложение, установленное в образе системы, может изменить свое поведение в зависимости от значения ресурса. Вместо жесткого кодирования значения ресурса во время сборки 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 или более поздней версии поддерживает правило сборки 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 актера , может переопределить ресурсы. Актер объявлен в теге именованного актера в конфигурации системы.
  • config_signature . Любое наложение, подписанное той же подписью, что и APK-файл overlay-config , может переопределить ресурсы. Конфигурация overlay объявляется в теге 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 или более поздней версии исключена возможность использования статических наложений, чтобы влиять на значения ресурсов, считываемых во время установки пакета. В обычном случае использования статических наложений для изменения значения логических значений, которые настраивают состояние включенного компонента, используйте тег SystemConfig <component-override> (новинка в 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