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 obrazie systemu może zmieniać swoje działanie w zależności od wartości zasobu. Zamiast zakodowywać wartość zasobu na stałe w czasie kompilacji, RRO zainstalowany na innej partycji może zmieniać wartości zasobów aplikacji w czasie działania.

RRO można włączać i wyłączać. Możesz programowo ustawić stan włączenia/wyłączenia, aby przełączać możliwość zmiany wartości zasobów przez RRO. 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ładki na zasoby zdefiniowane w pakiecie docelowym. Gdy aplikacja próbuje rozpoznać wartość zasobu w pakiecie docelowym, zamiast tego zwracana jest wartość zasobu nakładki, do którego 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 RRO ma zastąpić.

  • Wartość opcjonalnego atrybutu android:targetName określa nazwę podzbioru zasobów docelowego pakietu, które można nakładać, a które RRO ma nakładać. Jeśli element docelowy nie definiuje zestawu zasobów, które można nakładać, ten atrybut nie powinien być obecny.

Poniższy kod pokazuje 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ć się na kod, więc nie mogą zawierać plików DEX. Dodatkowo atrybut android:hasCode tagu <application> w pliku manifestu musi mieć wartość false.

Określ mapę 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, i ich wartości zastępczych, a następnie ustawienie wartości atrybutu android:resourcesMap tagu 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 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) próbę deduplikacji konfiguracji zasobów o tej samej wartości (--no-resource-deduping) i usuwania zasobów bez konfiguracji domyślnych (--no-resource-removal). Poniższy kod przedstawia przykład pliku Android.bp.

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

Rozwiązywanie zasobów

Jeśli zasób docelowy lub zasób nakładki ma wiele konfiguracji zdefiniowanych dla zasobu, o który wysyłane jest zapytanie, środowisko wykonawcze zasobów zwraca wartość konfiguracji, która najlepiej pasuje do konfiguracji urządzenia. Aby określić, która konfiguracja jest najlepiej dopasowana, połącz zestaw konfiguracji zasobów nakładki z zestawem konfiguracji zasobów docelowych, a następnie postępuj zgodnie ze zwykłym procesem rozwiązywania zasobów (szczegółowe informacje znajdziesz w artykule Jak Android znajduje najlepiej dopasowany zasób).

Jeśli np. nakładka określa wartość konfiguracji drawable-en, a element docelowy określa wartość drawable-en-port, drawable-en-port, drawable-en-port jest lepiej dopasowany, więc w czasie działania wybierana jest wartość konfiguracji elementu docelowego drawable-en-port. Aby zastąpić wszystkie konfiguracje drawable-en, nakładka musi definiować wartość dla każdej konfiguracji drawable-en, którą określa środowisko docelowe.

Nakładki mogą odwoływać się do własnych zasobów, a ich działanie może się różnić w zależności od wersji Androida.

  • W Androidzie 11 lub nowszym każda nakładka ma własną zarezerwowaną przestrzeń identyfikatorów zasobów, która nie pokrywa się z przestrzenią identyfikatorów zasobów docelowych ani z przestrzeniami identyfikatorów zasobów innych nakładek. Dzięki temu nakładki odwołujące się do własnych zasobów działają zgodnie z oczekiwaniami.

  • W Androidzie 10 lub starszym nakładki i pakiety docelowe mają tę samą przestrzeń identyfikatorów zasobów, co może powodować konflikty i nieoczekiwane działanie, gdy próbują odwoływać się do własnych zasobów za pomocą składni @type/name.

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

Nakładki można włączać i wyłączać ręcznie oraz programowo.

Ręczne wyłączanie i włączanie nakładek

Aby ręcznie włączyć i zweryfikować RRO, uruchom to polecenie:

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

Umożliwia to RRO dla użytkownika systemu (userId = 0), który jest właścicielem SystemUI. Ta instrukcja nie ma wpływu na aplikacje uruchamiane przez użytkownika na pierwszym planie (userId = 10). Aby włączyć RRO dla użytkownika na pierwszym planie, użyj parametru -–user 10:

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

Programowe włączanie i wyłączanie nakładek

Użyj interfejsu OverlayManager API, aby włączać i wyłączać nakładki modyfikowalne (pobierz interfejs API za pomocą Context#getSystemService(Context.OVERLAY_SERVICE)). Nakładkę może włączyć tylko pakiet, do którego jest kierowana, lub pakiet z uprawnieniem android.permission.CHANGE_OVERLAY_PACKAGES. Gdy nakładka jest włączana lub wyłączana, zdarzenia zmiany konfiguracji są propagowane do pakietu docelowego, a aktywności docelowe są ponownie uruchamiane.

Ograniczanie zasobów, które można nakładać

W Androidzie 10 lub nowszym tag XML <overlayable> udostępnia zestaw zasobów, które mogą być zastępowane przez nakładki RRO. W tym przykładzie plikures/values/overlayable.xml zasoby string/foointeger/bar służą do określania wyglądu urządzenia. Aby nałożyć te zasoby, nakładka musi wyraźnie kierować na zbiór 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ć w pakiecie unikalną nazwę. Na przykład:

  • Dwa różne pakiety mogą definiować <overlayable name="foo">.

  • Nie można mieć 2 bloków <overlayable name="foo"> w jednym pliku APK.

Poniższy kod pokazuje 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ę:

  • Musisz podać wartość targetName.

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

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

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

Ograniczanie zasad

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ć uwzględnione zasoby. Obsługiwane typy obejmują:

  • 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 partycję dostawcy może zastąpić zasoby.
  • product. Każda nakładka na podział produktów może zastąpić zasoby.
  • oem. Każda nakładka na partycji 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 named-actor w pliku system config.
  • config_signature. Każda nakładka podpisana tym samym podpisem co plik APK overlay-config może zastąpić zasoby. Konfiguracja nakładki jest zadeklarowana w tagu overlay-config-signature w konfiguracji systemu.

Poniższy kod pokazuje 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 separatorów. Jeśli określono wiele zasad, nakładka musi spełniać tylko jedne z nich, aby zastąpić zasoby wymienione w tagu <policy>.

Konfigurowanie nakładek

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 Androidem 11 lub nowszym mogą używać pliku OverlayConfig (config.xml) zamiast atrybutów pliku manifestu. Używanie pliku nakładki to zalecana metoda w przypadku nakładek.

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

Używanie obiektu OverlayConfig

W Androidzie 11 lub nowszym możesz użyć OverlayConfig, aby skonfigurować możliwość zmiany, stan domyślny i priorytet nakładek. Aby skonfigurować nakładkę, utwórz lub zmodyfikuj plik znajdujący się w lokalizacji 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/ partycji, w której jest konfigurowana. Poniższy kod zawiera przykład 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 jest domyślnie włączona (domyślnie false). Opcjonalny atrybut mutable określa, czy nakładka jest modyfikowalna i czy jej stan włączenia można zmienić programowo w czasie wykonywania (domyślnie true). Nakładki, które nie są wymienione w pliku konfiguracyjnym, są modyfikowalne i domyślnie wyłączone.

Pierwszeństwo nakładki

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

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

Scalanie plików

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

Używanie atrybutów pliku manifestu lub statycznych nakładek RRO

W Androidzie 10 i starszych wersjach niezmienność i pierwszeństwo nakładki są konfigurowane za pomocą tych atrybutów 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 zmienić, co uniemożliwia jej wyłączenie.

  • android:priority. Wartość tego atrybutu liczbowego (który wpływa tylko na statyczne nakładki) określa priorytet nakładki, gdy wiele statycznych nakładek jest kierowanych na tę samą wartość zasobu. Im wyższa liczba, tym wyższy priorytet.

Poniższy kod zawiera przykład 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

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

Dodatkowo Android 11 lub nowszy usuwa możliwość używania statycznych nakładek do wpływania na wartości zasobów odczytywanych podczas instalacji pakietu. W przypadku typowego zastosowania statycznych nakładek do zmiany wartości zmiennych logicznych, które konfigurują stan włączenia komponentu, użyj tagu <component-override> SystemConfig (nowość w Androidzie 11).

Nakładki debugowania

Aby ręcznie włączać, wyłączać i zrzucać nakładki, użyj tego polecenia powłoki menedżera nakładek.

adb shell cmd overlay

Użycie enable bez określenia użytkownika wpływa na bieżącego użytkownika, czyli użytkownika systemu (userId = 0), który jest właścicielem interfejsu systemu. Nie ma to wpływu na użytkownika pierwszego planu (userId = 10), który jest właścicielem aplikacji. Aby włączyć RRO dla użytkownika na pierwszym planie, użyj parametru –-user 10:

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

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 prawidłowo, znajdź odpowiedni plik idmap w folderze /data/resource-cache/, a następnie uruchom to polecenie.

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

To polecenie wyświetla 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