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

Наложение ресурсов времени выполнения (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 .

Включить/отключить наложения

Наложения можно включать/отключать вручную и программно.

Вручную отключите или включите наложения

Чтобы вручную включить и проверить RRO, выполните:

adb shell cmd overlay enable --user current com.example.carrro
adb shell cmd overlay list --user current | grep -i com.example com.example.carrro

Это включает RRO для системного пользователя ( userId = 0 ), которому принадлежит SystemUI. Эта инструкция не влияет на приложения, запущенные пользователем переднего плана ( userId = 10 ). Чтобы включить RRO для пользователя переднего плана, используйте параметр -–user 10 :

adb shell cmd overlay enable --user 10 com.example.carrro

Программное включение или отключение наложений

Используйте 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 в системной конфигурации.
  • config_signature . Любой оверлей, подписанный той же подписью, что и apk overlay-config, может переопределить ресурсы. 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 не влияют на оверлеи, расположенные в разделе. Определение файла конфигурации оверлея в любом разделе обеспечивает приоритет раздела оверлея.

Кроме того, Android 11 или выше удаляет возможность использования статических наложений для воздействия на значения ресурсов, считываемых во время установки пакета. Для общего случая использования статических наложений для изменения значения булевых значений, которые настраивают состояние включенного компонента, используйте тег SystemConfig <component-override> (новый в Android 11).

Отладочные наложения

Чтобы вручную включить, отключить и сбросить оверлеи, используйте следующую команду оболочки менеджера оверлеев.

adb shell cmd overlay

Использование enable без указания пользователя влияет на текущего пользователя, то есть на системного пользователя ( userId = 0 ), который владеет System UI. Это не влияет на пользователя переднего плана ( userId = 10 ), который владеет приложениями. Чтобы включить RRO для пользователя переднего плана, используйте параметр –-user 10 :

adb shell cmd overlay enable --user 10 com.example.carrro

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