Zmień wartość zasobów aplikacji w czasie jej wykonywania

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

RRO można włączyć lub wyłączyć. Można programowo ustawić stan włączania/wyłączania, aby przełączać zdolność RRO do zmiany wartości zasobów. RRO są domyślnie wyłączone (jednak statyczne RRO są domyślnie włączone).

Zasoby nakładki

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

Skonfiguruj manifest

Pakiet jest uważany za pakiet RRO, jeśli zawiera znacznik <overlay> jako dziecko znacznika <manifest> .

  • Wartość wymaganego atrybutu android:targetPackage określa nazwę pakietu, który RRO zamierza nałożyć.

  • Wartość opcjonalnego atrybutu android:targetName określa nazwę nakładalnego podzbioru zasobów pakietu docelowego, który RRO zamierza nałożyć. Jeśli element docelowy nie definiuje zestawu zasobów, na który można się nakładać, ten atrybut nie powinien występować.

Poniższy kod przedstawia przykładową nakładkę 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ą mieć plików DEX. Ponadto atrybut android:hasCode znacznika <application > w manifeście musi mieć wartość false .

Zdefiniuj mapę zasobów

W systemie Android 11 lub nowszym zalecanym mechanizmem definiowania mapy zasobów nakładki jest utworzenie pliku w katalogu res/xml pakietu nakładki, wyliczenie zasobów docelowych, które mają zostać nałożone oraz wartości ich zastępowania, a następnie ustawienie wartości android:resourcesMap atrybut znacznika manifestu <overlay> na odwołanie do pliku mapowania zasobów.

Poniższy kod przedstawia 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ższy kod przedstawia przykładowy manifest 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>

Zbuduj pakiet

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

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

Rozwiąż zasoby

Jeśli dla zasobu docelowego lub zasobu nakładkowego zdefiniowano wiele konfiguracji 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 najlepiej pasującą konfiguracją, połącz zestaw konfiguracji zasobów nakładki z zestawem konfiguracji zasobów docelowych, a następnie postępuj zgodnie ze zwykłym przepływem rozwiązywania zasobów (aby uzyskać szczegółowe informacje, zobacz Jak system Android znajduje najlepiej pasujący zasób ).

Na przykład, jeśli nakładka definiuje wartość dla konfiguracji drawable-en , a element docelowy definiuje wartość dla drawable-en-port port, drawable-en-port ma lepsze dopasowanie, więc wartość konfiguracji docelowej drawable-en-port wynosi wybrany w czasie wykonywania. Aby nałożyć na siebie wszystkie konfiguracje drawable-en , nakładka musi definiować wartość dla każdej konfiguracji drawable-en rysowania zdefiniowanej przez element docelowy.

Nakładki mogą odwoływać się do własnych zasobów, zachowując się inaczej w zależności od wersji Androida.

  • W systemie Android 11 lub nowszym każda nakładka ma własną zarezerwowaną przestrzeń identyfikatora zasobu, która nie nakłada się na docelową przestrzeń identyfikatora zasobu ani inne przestrzenie identyfikatora zasobów nakładki, więc nakładki odwołujące się do własnych zasobów działają zgodnie z oczekiwaniami.

  • W systemie Android 10 lub starszym nakładki i pakiety docelowe korzystają z tej samej przestrzeni identyfikatorów zasobów, co może powodować kolizje i nieoczekiwane zachowanie podczas próby odwoływania się do własnych zasobów przy użyciu składni @type/name .

Włącz/wyłącz nakładki

Użyj interfejsu API OverlayManager , aby włączyć i wyłączyć modyfikowalne nakładki (pobierz interfejs API za pomocą Context#getSystemService(Context.OVERLAY_SERVICE) ). Nakładkę można włączyć tylko przez pakiet, na który jest przeznaczony, lub przez pakiet z uprawnieniem android.permission.CHANGE_OVERLAY_PACKAGES . Kiedy nakładka jest włączona lub wyłączona, zdarzenia zmiany konfiguracji są propagowane do pakietu docelowego i działania docelowe są ponownie uruchamiane.

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

W systemie Android 10 lub nowszym tag XML <overlayable> udostępnia zestaw zasobów, na które RRO mogą nakładać. W poniższym przykładzie pliku res/values/overlayable.xml string/foo i integer/bar to zasoby używane do nadawania wyglądu urządzeniu; aby nałożyć te zasoby, nakładka musi jawnie kierować kolekcję zasobów, które można nałożyć, 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 tag musi mieć unikalną nazwę w pakiecie. Na przykład jest to:

  • OK dla dwóch różnych pakietów, aby oba zdefiniować <overlayable name="foo"> .

  • Nie jest w porządku, aby pojedynczy plik APK zawierał dwa bloki <overlayable name="foo"> .

Poniższy kod przedstawia 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 kierowane na tę aplikację:

  • Należy określić targetName .

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

  • Można kierować tylko na jedną nazwę <overlayable> .

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

Ogranicz zasady

Użyj tagu <policy> , aby wymusić ograniczenia dotyczące zasobów, które można nakładać. Atrybut type określa, które zasady musi spełniać nakładka, aby zastąpić zawarte w niej zasoby. Obsługiwane typy obejmują następujące.

  • public . Każda nakładka może zastąpić zasób.
  • system . Każda nakładka na partycję systemową może zastąpić zasoby.
  • vendor . Każda nakładka na partycję dostawcy może zastąpić zasoby.
  • product . Każda nakładka na partycję produktu może zastąpić zasoby.
  • oem . Każda nakładka na partycję OEM może zastąpić zasoby.
  • odm . Każda nakładka na partycję 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 nazwanego aktora w konfiguracji systemu.
  • config_signature . Każda nakładka podpisana tym samym podpisem co apk overlay-config może zastąpić zasoby. Overlay-config jest zadeklarowany w tagu overlay-config-signature w konfiguracji systemu.

Poniższy 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 pionowych kresek (|) jako znaków oddzielających. Jeśli określono wiele zasad, nakładka musi spełniać tylko jedną zasadę, aby zastąpić zasoby wymienione w tagu <policy> .

Skonfiguruj nakładki

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

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

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

Użyj OverlayConfig

W systemie Android 11 lub nowszym możesz użyć OverlayConfig aby skonfigurować zmienność, stan domyślny i priorytet nakładek. Aby skonfigurować nakładkę, utwórz lub zmodyfikuj plik znajdujący się w partition/overlay/config/config.xml , gdzie partition jest partycją nakładki, która ma zostać skonfigurowana. Aby nakładka mogła zostać skonfigurowana, musi znajdować się w katalogu overlay/ partycji, w której nakładka jest skonfigurowana. Poniższy kod przedstawia przykładowy 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>"

Znacznik <overlay> wymaga atrybutu package wskazującego, który pakiet nakładki jest konfigurowany. Opcjonalny atrybut enabled kontroluje, czy nakładka jest domyślnie włączona (wartość domyślna to false ). Opcjonalny atrybut mutable kontroluje, czy nakładka jest modyfikowalna i czy jej stan włączenia może zostać programowo zmieniony w czasie wykonywania (wartość domyślna to true ). Nakładki, które nie są wymienione w pliku konfiguracyjnym, są domyślnie modyfikowalne i wyłączone.

Pierwszeństwo nakładki

Gdy wiele nakładek zastępuje te same zasoby, ważna jest kolejność nakładek. Nakładka ma większy priorytet niż nakładki z konfiguracjami poprzedzającymi własną konfigurację. Kolejność pierwszeństwa nakładek w różnych partycjach (od najmniejszego do największego) jest następująca.

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

Połącz pliki

Użycie znaczników <merge> umożliwia połączenie innych plików konfiguracyjnych w określonej pozycji z plikiem konfiguracyjnym. Atrybut path znacznika reprezentuje ścieżkę pliku do scalania względem katalogu zawierającego pliki konfiguracyjne nakładki.

Użyj atrybutów manifestu/statycznych RRO

W systemie Android 10 lub starszym niezmienność i pierwszeństwo nakładki są konfigurowane przy użyciu następujących atrybutów manifestu.

  • android:isStatic . Gdy wartość tego atrybutu logicznego jest ustawiona na true , nakładka jest domyślnie włączona i jest niezmienna, co zapobiega wyłączeniu nakładki.

  • android:priority . Wartość tego atrybutu numerycznego (który wpływa tylko na nakładki statyczne) konfiguruje pierwszeństwo nakładki, gdy wiele nakładek statycznych jest ukierunkowanych na tę samą wartość zasobu. Wyższa liczba oznacza wyższy priorytet.

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

W systemie Android 11 lub nowszym, jeśli plik konfiguracyjny znajduje się w partition/overlay/config/config.xml , nakładki są konfigurowane przy użyciu tego pliku, a android:isStatic i android:priority nie mają wpływu na nakładki znajdujące się na partycji. Zdefiniowanie pliku konfiguracyjnego nakładki na dowolnej partycji wymusza pierwszeństwo partycji nakładki.

Ponadto Android 11 lub nowszy usuwa możliwość używania statycznych nakładek wpływających na wartości zasobów odczytywane podczas instalacji pakietu. W typowym przypadku użycia nakładek statycznych do zmiany wartości wartości logicznych konfigurujących stan włączenia komponentu należy użyć tagu <component-override> SystemConfig (nowość w systemie Android 11).

Debuguj nakładki

Aby ręcznie włączyć, wyłączyć i zrzucić nakładki, użyj następującego polecenia powłoki menedżera nakładek.

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 /data/resource-cache/ . Jeśli nakładka nie działa poprawnie, znajdź odpowiedni plik idmap dla swojej nakładki w /data/resource-cache/ , a następnie uruchom następujące polecenie.

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

To polecenie drukuje mapowanie zasobów, jak pokazano 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