Zmienianie wartości zasobów aplikacji w czasie działania

Nakładka zasobów środowiska wykonawczego (RRO) to pakiet, który zmienia wartości zasobów pakietu docelowego w czasie działania. Na przykład aplikacja zainstalowana w systemie może zmieniać swoje działanie w zależności od wartości zasobu. Zamiast kodować na stałe wartość zasobu w momencie kompilacji, RRO zainstalowana w innej partycji może zmieniać wartości zasobów aplikacji w czasie działania.

RRO można włączyć lub wyłączyć. Możesz automatycznie ustawiać stan włączenia/wyłączenia, aby przełączać możliwość zmiany wartości zasobów przez RRO. Reguły RRO są domyślnie wyłączone (ale statyczne RRO są domyślnie włączone).

Zasoby nakładek

Przesłonięcia działają poprzez mapowanie zasobów zdefiniowanych w pakiecie nakładki na zasoby zdefiniowane w pakiecie docelowym. Gdy aplikacja próbuje rozpoznać wartość zasobu w pakiecie docelowym, zwracana jest wartość zasobu nakładki, na który jest mapowany zasób docelowy.

Konfigurowanie pliku manifestu

Pakiet jest uznawany za pakiet RRO, jeśli zawiera tag <overlay> jako element podrzędny tagu <manifest>.

  • Wartość wymaganego atrybutu android:targetPackage określa nazwę pakietu, który ma być nakładany przez RRO.

  • Wartość opcjonalnego atrybutu android:targetName określa nazwę nakładanego podzbioru zasobów pakietu docelowego, który RRO ma nałożyć. Jeśli miejsce docelowe nie definiuje zbioru zasobów, który można nakładać, ten atrybut nie powinien być obecny.

Poniższy kod to przykładowa nakładka 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>

Nakładki nie mogą nakładać kodu, więc nie mogą zawierać plików DEX. Dodatkowo atrybut android:hasCode tagu <application> w pliku manifestu musi mieć wartość false.

Definiowanie mapy zasobów

W Androidzie 11 lub nowszym zalecanym mechanizmem definiowania mapy zasobów nakładki jest utworzenie pliku w katalogu res/xml pakietu nakładki, wyliczenie docelowych zasobów, które mają być nakładane, oraz ich wartości zastępcze, a następnie ustawienie wartości atrybutu android:resourcesMap tagu manifestu <overlay> jako odwołania do pliku mapowania zasobów.

Poniższy kod to przykładowy plik 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>

Poniżej znajduje się przykładowy plik manifestu nakładki.

<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>

Tworzenie pakietu

Android 11 lub nowszy obsługuje regułę kompilacji Soong dla nakładek, która uniemożliwia narzędziu Android Asset Packaging Tool 2 (AAPT2) deduplikowanie konfiguracji zasobów o tej samej wartości (--no-resource-deduping) i usuwaniu zasobów bez konfiguracji domyślnych (--no-resource-removal). Poniższy kod zawiera przykładowy plik Android.bp.

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

Rozwiąż problem z zasobami

Jeśli zasób docelowy lub zasób nakładki ma wiele konfiguracji zdefiniowanych dla zasobu, którego dotyczy zapytanie, środowisko wykonawcze zasobów zwraca wartość konfiguracji, która najlepiej pasuje do konfiguracji urządzenia. Aby określić, która konfiguracja jest najbardziej zbliżona do konfiguracji docelowej, scal zestaw konfiguracji zasobów nakładów z zestawem konfiguracji zasobów docelowych, a potem postępuj zgodnie ze zwykłą procedurą rozwiązywania zasobów (szczegółowe informacje znajdziesz w artykule Jak Android znajduje najbardziej pasujący zasób).

Jeśli np. nakładka definiuje wartość konfiguracji drawable-en, a cel – wartość dla parametru drawable-en-port, lepiej pasuje drawable-en-port, więc wartość konfiguracji docelowej drawable-en-port jest wybierana w czasie działania. Aby możliwe było nałożenie wszystkich konfiguracji drawable-en, nakładka musi określać wartość dla każdej konfiguracji drawable-en zdefiniowanej przez cel.

Nakładki mogą wykorzystywać własne zasoby, a ich działanie różni się w zależności od wersji Androida.

  • W Androidzie 11 lub nowszym każda nakładka ma własny rezerwowany identyfikator zasobu, który nie pokrywa się z identyfikatorem zasobu docelowego ani z identyfikatorami zasobów innych nakładek, dzięki czemu nakładki odwołujące się do własnych zasobów działają zgodnie z oczekiwaniami.

  • W Androidzie 10 i starszych wersjach nakładki i pakiety docelowe współdzielą tę samą przestrzeń identyfikatora zasobu, co może powodować kolizje i nieoczekiwane działanie przy odwoływaniu się do własnych zasobów przy użyciu składni @type/name.

Włączanie i wyłączanie nakładek

Aby włączać i wyłączać zmienne nakładki, użyj interfejsu API OverlayManager (skorzystanie z interfejsu API za pomocą Context#getSystemService(Context.OVERLAY_SERVICE)). Nakładki można włączyć tylko przez pakiet, na który jest kierowana, lub przez pakiet z uprawnieniem android.permission.CHANGE_OVERLAY_PACKAGES. Gdy nakład jest włączony lub wyłączony, zdarzenia zmiany konfiguracji są propagowane do pakietu docelowego i ponownego uruchomienia docelowych aktywności.

Ogranicz zasoby, które można nakładać

W Androidzie 10 lub nowszym tag XML <overlayable> udostępnia zestaw zasobów, które mogą być nakładane przez RRO. W tym przykładowym pliku res/values/overlayable.xml string/foointeger/bar to zasoby używane do tworzenia motywów wyglądu urządzenia. Aby nakładać te zasoby, nakładka musi wyraźnie wskazywać kolekcję zasobów nakładających się na nią według nazwy.

<!-- 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>

Plik APK może definiować wiele tagów <overlayable>, ale każdy z nich musi mieć unikalną nazwę w pakiecie. Na przykład:

  • OK w przypadku 2 różnych pakietów, które definiują <overlayable name="foo">.

  • Niedopuszczalne jest, aby jeden plik APK zawierał 2 bloki <overlayable name="foo">.

Poniższy kod to przykład nakładki w pliku 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>

Gdy aplikacja definiuje tag <overlayable>, nakładki kierowania na tę aplikację:

  • Musisz określić wartość targetName.

  • Może nakładać się tylko na zasoby wymienione w tagu <overlayable>.

  • Może kierować reklamy tylko na 1 nazwę <overlayable>.

Nie możesz włączyć nakładki kierowanej na pakiet, który udostępnia zasoby nakładowe, ale nie używa tagu android:targetName do kierowania na konkretny tag <overlayable>.

Zasady ograniczeń

Użyj tagu <policy>, aby nałożyć ograniczenia na zasoby nakładowe. Atrybut type określa zasady, które musi spełniać nakładka, aby zastąpić uwzględnione zasoby. Obsługiwane typy:

  • public. Każda nakładka może zastąpić zasób.
  • system. Każda nakładka na partycji systemowej może zastąpić zasoby.
  • vendor. Każda nakładka na partycji dostawcy może zastąpić zasoby.
  • product. Każde nakładanie się na partycję usługi może zastąpić zasoby.
  • oem. Każde nakładanie się na partycję OEM może zastąpić zasoby.
  • odm. Każda nakładka na partycji odm może zastąpić zasoby.
  • signature. Każda nakładka podpisana tym samym podpisem co docelowy plik APK może zastąpić zasoby.
  • actor. Każda nakładka podpisana tym samym podpisem co plik APK aktora może zastąpić zasoby. Aktor jest zadeklarowany w tagu named-actor w konfiguracji systemu.
  • config_signature. Każda nakładka podpisana tym samym podpisem co plik overlay-config apk może zastąpić zasoby. Nakładka-config jest deklarowana w tagu overlay-config-signature w konfiguracji systemu.

Ten kod przedstawia przykładowy tag <policy> w pliku 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>

Aby określić wiele zasad, użyj kresek pionowych (|) jako znaków rozdzielających. Jeśli określono wiele zasad, nakładka musi spełniać tylko jedną z nich, aby zastąpić zasoby wymienione w tagu <policy>.

Konfigurowanie nakładek

Android obsługuje różne mechanizmy konfigurowania możliwości zmiany, domyślnego stanu i priorytetu nakładek w zależności od wersji Androida.

  • Urządzenia z Androidem 11 lub nowszym mogą używać pliku OverlayConfig (config.xml) zamiast atrybutów pliku manifestu. Zalecaną metodą w przypadku nakładek jest użycie pliku nakładki.

  • Wszystkie urządzenia mogą używać atrybutów pliku manifestu (android:isStaticandroid:priority) do konfigurowania statycznych RRO.

Używanie obiektu OverlayConfig

W Androidzie 11 lub nowszym za pomocą OverlayConfig możesz konfigurować zmienność, stan domyślny i priorytet nakładek. Aby skonfigurować nakładkę, utwórz lub zmodyfikuj plik znajdujący się w folderze partition/overlay/config/config.xml, gdzie partition to partycja nakładki, którą chcesz skonfigurować. Aby można było skonfigurować nakładkę, musi ona znajdować się w katalogu overlay/ na partycji, na której jest skonfigurowana. Poniżej znajduje się przykładowy kod 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>"

Tag <overlay> wymaga atrybutu package, który wskazuje, który pakiet nakładek jest konfigurowany. Opcjonalny atrybut enabled określa, czy nakładka ma być domyślnie włączona (domyślnie false). Opcjonalny atrybut mutable określa, czy nakładkę można zmieniać i czy jej stan można zmieniać automatycznie w czasie działania (domyślnie jest to true). Nakładki, których nie ma w pliku konfiguracji, można domyślnie zmienić i wyłączyć.

Pierwszeństwo nakładek

Gdy wiele nakładek zastępuje te same zasoby, kolejność nakładek ma znaczenie. Nakładka ma wyższy priorytet niż nakładki z konfiguracjami, które mają własną konfigurację. Kolejność nakładek w różnych partycjach (od najmniej do najwyższego priorytetu) jest taka:

  • system
  • vendor
  • odm
  • oem
  • product
  • system_ext

Scal pliki

Użycie tagów <merge> umożliwia scalanie innych plików konfiguracji w określonym miejscu z tym plikiem. Atrybut path tagu reprezentuje ścieżkę pliku do scalenia względem katalogu zawierającego pliki konfiguracji nakładki.

Używaj atrybutów pliku manifestu/statycznych RRO

W Androidzie 10 lub starszym niezmienność i pierwszeństwo nakładki są konfigurowane za pomocą tych atrybutów pliku manifestu.

  • android:isStatic. Jeśli wartość tego atrybutu logicznego jest ustawiona na true, nakładka jest domyślnie włączona i nie można jej wyłączyć.

  • android:priority. Wartość tego atrybutu liczbowego (który ma wpływ tylko na nakładki statyczne) określa kolejność nakładek, gdy wiele nakładek statycznych jest kierowanych na tę samą wartość zasobu. Wyższa liczba oznacza większą priorytetowość.

Poniżej znajduje się przykładowy kod 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>

Zmiany w Androidzie 11

Na Androidzie 11 lub nowszym, jeśli plik konfiguracji znajduje się w regionie partition/overlay/config/config.xml, nakładki są konfigurowane za pomocą tego pliku, a android:isStatic i android:priority nie mają wpływu na nakładki znajdujące się na partycji. Zdefiniowanie pliku konfiguracji nakładki w dowolnej partycji powoduje zastosowanie pierwszeństwa partycji nakładki.

Oprócz tego w Androidzie 11 lub nowszym nie można używać statycznych nakładek do wpływania na wartości zasobów odczytywanych podczas instalowania pakietu. W przypadku typowego zastosowania polegającego na używaniu statycznych nakładek do zmiany wartości zmiennych logicznych, które konfigurują stan włączenia komponentu, użyj tagu <component-override> SystemConfig (nowy w Androidzie 11).

Debugowanie nakładek

Aby ręcznie włączyć, wyłączyć i wykonać zrzut nakładek, użyj poniższego polecenia powłoki menedżera nakładki.

adb shell cmd overlay

OverlayManagerService używa idmap2 do mapowania identyfikatorów zasobów w pakiecie docelowym na identyfikatory zasobów w pakiecie nakładki. Wygenerowane mapowania identyfikatorów są przechowywane w usłudze /data/resource-cache/. Jeśli nakładka nie działa prawidłowo, znajdź odpowiedni plik idmap w /data/resource-cache/ i uruchom następujące polecenie.

adb shell idmap2 dump --idmap-path [file]

To polecenie powoduje wydrukowanie mapowania zasobów w sposób pokazany poniżej.

[target res id] - > [overlay res id] [resource name]
0x01040151 -> 0x01050001 string/config_dozeComponent
0x01040152 -> 0x01050002 string/config_dozeDoubleTapSensorType
0x01040153 -> 0x01050003 string/config_dozeLongPressSensorType