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 wykonywania. 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 czasie kompilacji, możesz zainstalować RRO na innej partycji i zmieniać wartości zasobów aplikacji w czasie wykonywania.

RRO można włączyć lub wyłączyć. Możesz ustawić stan włączenia/wyłączenia za pomocą kodu, aby włączyć lub wyłączyć możliwość zmiany wartości zasobu przez RRO. RRO są domyślnie wyłączone (statyczne RRO są jednak domyślnie włączone).

Zasoby nakładki

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

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ładalnego podzbioru zasobów z pakietu docelowego, który ma być nakładany przez RRO. Jeśli docelowe nie definiuje zbioru zasobów, na których można nakładać elementy, tego atrybutu nie ma.

Poniższy kod pokazuje przykład nakładki 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ą zawierać 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 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 pokazuje 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 dotyczącą nakładek, która zapobiega próbom usunięcia przez narzędzie Android Asset Packaging Tool 2 (AAPT2) duplikatów konfiguracji zasobów o tej samej wartości (--no-resource-deduping) i usuwaniu zasobów bez domyślnych konfiguracji (--no-resource-removal). Poniżej znajduje się przykładowy kod pliku Android.bp.

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

Rozwiązywanie problemów

Jeśli zasób docelowy lub zasób nakładki ma zdefiniowane większą liczbę konfiguracji dla zasobu, którego dotyczy zapytanie, środowisko uruchomieniowe zasobów zwraca wartość konfiguracji, która najlepiej pasuje do konfiguracji urządzenia. Aby określić, która konfiguracja jest najbardziej odpowiednia, scal zestaw konfiguracji zasobów nakładki z zestawem konfiguracji zasobów docelowych, a potem postępuj zgodnie ze zwykłym procesem rozwiązywania zasobów (szczegółowe informacje znajdziesz w artykule Jak Android znajduje najbardziej pasujący zasób).

Jeśli na przykład w nakładce zdefiniowano wartość konfiguracji drawable-en, a w docelowym elemencie drawable-en-port, to drawable-en-port ma lepsze dopasowanie, więc w czasie działania wybrana zostanie wartość konfiguracji docelowej drawable-en-port. Aby nakładać wszystkie konfiguracje drawable-en, musisz zdefiniować wartość dla każdej konfiguracji drawable-en określonej przez wartość docelową.

Nakładki mogą odwoływać się do własnych zasobów, przy czym 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łasny zastrzeżony 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 lub starszym nakładki i pakiety docelowe korzystają z tej samej przestrzeni identyfikatorów zasobów, co może powodować kolizje i nieoczekiwane działanie podczas próby odwołania 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 lub programowo.

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

Aby ręcznie włączyć i sprawdzać RRO, uruchom:

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. Te instrukcje nie mają 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

Aby włączać i wyłączać zmienne nakładki, użyj interfejsu API OverlayManager (pobierz interfejs API za pomocą Context#getSystemService(Context.OVERLAY_SERVICE)). Nakładkę może włączyć tylko pakiet, na który jest ona kierowana, lub 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.

Ograniczanie zasobów z możliwością nakładania

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:

  • Dopuszczalne, aby 2 różne pakiety definiowały <overlayable name="foo">.

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

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 kierowania na tę aplikację:

  • Musisz podać 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ładki, ale nie używa tagu android:targetName do kierowania na konkretny tag <overlayable>.

Zasady ograniczania

Użyj tagu <policy>, aby nałożyć ograniczenia na zasoby nakładane na inne zasoby. Atrybut type określa, które zasady 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żde nakładanie się na partycji dostawcy może zastąpić zasoby.
  • product. Każde nakładanie się na partycję produktu może zastąpić zasoby.
  • oem. Każde nakładanie się na partycji OEM może zastąpić zasoby.
  • odm. Każde nakładanie się na partycję utom 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żde nakładanie podpisane tym samym podpisem co plik APK aktora może zastąpić zasoby. Aktorka jest deklarowana w tagu named-actor w pliku systemowym .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 deklarowana w tagu overlay-config-signature w konfiguracji systemowej.

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 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. Zalecane jest używanie pliku z nakładką.

  • 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 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 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ładki 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 zmienna i czy można zmienić jej stan w czasie wykonywania programu (domyślnie true). Nakładki niewymienione w pliku konfiguracyjnym są domyślnie zmienne i wyłączone.

Priorytet nakładki

Gdy wiele nakładek zastępuje te same zasoby, kolejność nakładek jest ważna. Nakładka ma wyższy priorytet niż nakładki z konfiguracjami, które poprzedzają jej konfigurację. Kolejność nakładek w różnych partycjach (od najmniejszego do największego priorytetu) jest następująca:

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

Scalanie plików

Użycie tagów <merge> umożliwia scalanie innych plików konfiguracji w określonym miejscu w pliku konfiguracji. Atrybut path tagu reprezentuje ścieżkę do pliku, który ma zostać scalony względem katalogu zawierającego pliki konfiguracji nakładki.

Używanie atrybutów pliku manifestu lub 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. Im wyższa liczba, tym wyższy priorytet.

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

W Androidzie 11 lub nowszym, jeśli plik konfiguracji znajduje się w katalogu partition/overlay/config/config.xml, nakładki są konfigurowane za pomocą tego pliku, a pliky android:isStaticandroid: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.

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

Nakładki debugowania

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

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 System UI. Nie ma to wpływu na użytkownika na pierwszym planie (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 docelowym pakiecie na identyfikatory zasobów w pakiecie nakładki. Wygenerowane mapowania identyfikatorów są przechowywane w pliku /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 wypisuje 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