Lớp phủ tài nguyên thời gian chạy (RRO) là một gói thay đổi giá trị tài nguyên của gói mục tiêu trong thời gian chạy. Ví dụ: một ứng dụng được cài đặt trên hình ảnh hệ thống có thể thay đổi hành vi của ứng dụng đó dựa trên giá trị của tài nguyên. Thay vì mã hoá cứng giá trị tài nguyên tại thời điểm xây dựng, RRO được cài đặt trên một phân vùng khác có thể thay đổi các giá trị của tài nguyên ứng dụng trong thời gian chạy.
Bạn có thể bật hoặc tắt RRO. Bạn có thể đặt trạng thái bật/tắt theo phương thức lập trình để bật/tắt khả năng thay đổi giá trị tài nguyên của RRO. RRO bị tắt theo mặc định (tuy nhiên, RRO tĩnh sẽ được bật theo mặc định).
Tài nguyên lớp phủ
Lớp phủ hoạt động bằng cách liên kết các tài nguyên được xác định trong gói lớp phủ với các tài nguyên được xác định trong gói mục tiêu. Khi một ứng dụng cố gắng phân giải giá trị của tài nguyên trong gói đích, giá trị của tài nguyên lớp phủ mà tài nguyên đích được liên kết sẽ được trả về.
Thiết lập tệp kê khai
Một gói được coi là gói RRO nếu gói đó chứa thẻ <overlay>
dưới dạng thẻ con của thẻ <manifest>
.
Giá trị của thuộc tính
android:targetPackage
bắt buộc chỉ định tên của gói mà RRO dự định phủ lên.Giá trị của thuộc tính
android:targetName
không bắt buộc chỉ định tên của tập hợp con tài nguyên có thể phủ của gói mục tiêu mà RRO dự định phủ. Nếu mục tiêu không xác định một nhóm tài nguyên có thể phủ, thì thuộc tính này sẽ không xuất hiện.
Mã sau đây cho thấy một lớp phủ mẫu 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>
Lớp phủ không thể phủ mã, vì vậy, lớp phủ không thể có tệp DEX. Ngoài ra, bạn phải đặt thuộc tính android:hasCode
của thẻ <application
> trong tệp kê khai thành false
.
Xác định bản đồ tài nguyên
Trong Android 11 trở lên, cơ chế đề xuất để xác định bản đồ tài nguyên lớp phủ là tạo một tệp trong thư mục res/xml
của gói lớp phủ, liệt kê các tài nguyên mục tiêu cần được phủ lên và giá trị thay thế của các tài nguyên đó, sau đó đặt giá trị của thuộc tính android:resourcesMap
của thẻ kê khai <overlay>
thành tham chiếu đến tệp ánh xạ tài nguyên.
Mã sau đây cho thấy một tệp res/xml/overlays.xml
mẫu.
<?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>
Mã sau đây cho thấy một tệp kê khai lớp phủ mẫu.
<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>
Tạo gói
Android 11 trở lên hỗ trợ quy tắc bản dựng Soong cho các lớp phủ ngăn cản Công cụ đóng gói tài nguyên Android 2 (AAPT2) cố gắng loại bỏ cấu hình của các tài nguyên có cùng giá trị (--no-resource-deduping
) và xoá những tài nguyên không có cấu hình mặc định (--no-resource-removal
). Mã sau đây cho thấy một tệp Android.bp
ví dụ.
runtime_resource_overlay {
name: "ExampleOverlay",
sdk_version: "current",
}
Giải quyết tài nguyên
Nếu một tài nguyên mục tiêu hoặc tài nguyên lớp phủ có nhiều cấu hình được xác định cho tài nguyên đang được truy vấn, thì thời gian chạy tài nguyên sẽ trả về giá trị của cấu hình phù hợp nhất với cấu hình của cấu hình thiết bị. Để xác định cấu hình nào là cấu hình phù hợp nhất, hãy hợp nhất nhóm cấu hình tài nguyên lớp phủ vào nhóm cấu hình tài nguyên mục tiêu, sau đó làm theo quy trình phân giải tài nguyên thông thường (để biết thông tin chi tiết, hãy tham khảo phần Cách Android tìm tài nguyên phù hợp nhất).
Ví dụ: nếu lớp phủ xác định giá trị cho cấu hình drawable-en
và mục tiêu xác định một giá trị cho drawable-en-port
, thì drawable-en-port
sẽ có kết quả phù hợp hơn nên giá trị của cấu hình mục tiêu drawable-en-port
sẽ được chọn trong thời gian chạy. Để phủ mọi cấu hình drawable-en
, lớp phủ phải xác định một giá trị cho mỗi cấu hình drawable-en
mà mục tiêu xác định.
Lớp phủ có thể tham chiếu tài nguyên riêng với hành vi khác nhau giữa các bản phát hành Android.
Trong Android 11 trở lên, mỗi lớp phủ đều có không gian mã nhận dạng tài nguyên riêng được đặt trước, không trùng lặp với không gian mã nhận dạng tài nguyên mục tiêu hoặc các không gian mã nhận dạng tài nguyên lớp phủ khác. Vì vậy, các lớp phủ tham chiếu đến tài nguyên của riêng chúng sẽ hoạt động như mong đợi.
Trong Android 10 trở xuống, lớp phủ và gói mục tiêu có cùng không gian mã nhận dạng tài nguyên. Điều này có thể gây ra xung đột và hành vi ngoài dự kiến khi chúng cố gắng tham chiếu tài nguyên của chính mình bằng cú pháp
@type/name
.
Bật/tắt lớp phủ
Sử dụng API OverlayManager
để bật và tắt các lớp phủ có thể thay đổi (truy xuất giao diện API bằng cách sử dụng Context#getSystemService(Context.OVERLAY_SERVICE)
). Chỉ có thể bật lớp phủ bằng gói mà nó nhắm mục tiêu hoặc bằng một gói có quyền android.permission.CHANGE_OVERLAY_PACKAGES
. Khi bạn bật hoặc tắt một lớp phủ, các sự kiện thay đổi cấu hình sẽ truyền đến gói mục tiêu và khởi chạy lại các hoạt động mục tiêu.
Hạn chế tài nguyên có thể phủ
Trên Android 10 trở lên, thẻ XML <overlayable>
sẽ hiển thị một tập hợp tài nguyên mà RRO được phép phủ lên. Trong tệp res/values/overlayable.xml
ví dụ sau, string/foo
và integer/bar
là các tài nguyên dùng để tạo giao diện cho thiết bị; để phủ các tài nguyên này, một lớp phủ phải nhắm mục tiêu rõ ràng đến tập hợp các tài nguyên có thể phủ theo tên.
<!-- 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>
Một tệp APK có thể xác định nhiều thẻ <overlayable>
, nhưng mỗi thẻ phải có một tên riêng biệt trong gói. Ví dụ:
Có thể sử dụng hai gói khác nhau để cùng xác định
<overlayable name="foo">
.Một tệp APK không được có hai khối
<overlayable name="foo">
.
Đoạn mã sau đây cho thấy ví dụ về một lớp phủ trong tệp 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>
Khi một ứng dụng xác định thẻ <overlayable>
, các lớp phủ nhắm đến ứng dụng đó:
Phải chỉ định
targetName
.Chỉ có thể phủ những tài nguyên được liệt kê trong thẻ
<overlayable>
.Chỉ có thể nhắm đến một tên
<overlayable>
.
Bạn không thể bật lớp phủ nhắm đến một gói hiển thị các tài nguyên có thể phủ nhưng không sử dụng android:targetName
để nhắm đến một thẻ <overlayable>
cụ thể.
Hạn chế chính sách
Dùng thẻ <policy>
để thực thi các quy định hạn chế đối với tài nguyên có thể phủ. Thuộc tính type
chỉ định những chính sách mà lớp phủ phải đáp ứng để ghi đè các tài nguyên được đưa vào. Sau đây là các loại được hỗ trợ.
public
. Mọi lớp phủ đều có thể ghi đè tài nguyên.system
. Mọi lớp phủ trên phân vùng hệ thống đều có thể ghi đè các tài nguyên.vendor
. Mọi lớp phủ trên phân vùng của nhà cung cấp đều có thể ghi đè tài nguyên.product
. Mọi lớp phủ trên phân vùng sản phẩm đều có thể ghi đè các tài nguyên.oem
. Mọi lớp phủ trên phân vùng OEM đều có thể ghi đè các tài nguyên.odm
. Mọi lớp phủ trên phân vùng odm đều có thể ghi đè các tài nguyên.signature
. Mọi lớp phủ được ký bằng cùng một chữ ký với APK mục tiêu đều có thể ghi đè các tài nguyên.actor
. Mọi lớp phủ được ký có cùng chữ ký với APK actor (thực thể) đều có thể ghi đè các tài nguyên. Thành phần này được khai báo trong thẻ named-actor trong cấu hình hệ thống.config_signature
. Mọi lớp phủ được ký bằng cùng một chữ ký với tệp APK overlay-config đều có thể ghi đè các tài nguyên. Lớp phủ-config được khai báo trong thẻ overlay-config-signature trong cấu hình hệ thống.
Đoạn mã sau đây cho thấy một thẻ <policy>
mẫu trong tệp 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>
Để chỉ định nhiều chính sách, hãy sử dụng thanh dọc (|) làm ký tự phân tách.
Khi bạn chỉ định nhiều chính sách, một lớp phủ chỉ cần đáp ứng một chính sách để ghi đè các tài nguyên đã liệt kê trong thẻ <policy>
.
Định cấu hình lớp phủ
Android hỗ trợ nhiều cơ chế để định cấu hình khả năng thay đổi, trạng thái mặc định và mức độ ưu tiên của lớp phủ tuỳ thuộc vào phiên bản phát hành Android.
Các thiết bị chạy Android 11 trở lên có thể dùng tệp
OverlayConfig
(config.xml
) thay vì các thuộc tính của tệp kê khai. Bạn nên sử dụng tệp lớp phủ cho lớp phủ.Tất cả thiết bị đều có thể sử dụng các thuộc tính tệp kê khai (
android:isStatic
vàandroid:priority
) để định cấu hình RRO tĩnh.
Sử dụng OverlayConfig
Trên Android 11 trở lên, bạn có thể sử dụng OverlayConfig
để định cấu hình khả năng thay đổi, trạng thái mặc định và mức độ ưu tiên của lớp phủ. Để định cấu hình lớp phủ, hãy tạo hoặc sửa đổi tệp nằm tại partition/overlay/config/config.xml
, trong đó partition
là phân vùng của lớp phủ cần định cấu hình. Để được định cấu hình, lớp phủ phải nằm trong thư mục overlay/
của phân vùng mà lớp phủ được định cấu hình. Mã sau đây cho thấy ví dụ về 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>"
Thẻ <overlay>
yêu cầu thuộc tính package
cho biết gói lớp phủ nào đang được định cấu hình. Thuộc tính enabled
không bắt buộc kiểm soát việc lớp phủ có được bật theo mặc định hay không (mặc định là false
). Thuộc tính mutable
không bắt buộc kiểm soát việc lớp phủ có thể thay đổi hay không và có thể có trạng thái bật theo cách lập trình trong thời gian chạy (mặc định là true
). Lớp phủ không được liệt kê trong tệp cấu hình có thể thay đổi và tắt theo mặc định.
Mức độ ưu tiên của lớp phủ
Khi nhiều lớp phủ ghi đè cùng một tài nguyên, thứ tự của các lớp phủ là rất quan trọng. Lớp phủ có mức độ ưu tiên cao hơn so với các lớp phủ có cấu hình trước cấu hình của chính lớp phủ đó. Thứ tự ưu tiên của lớp phủ trong các phân vùng khác nhau (từ thấp nhất đến cao nhất) như sau.
system
vendor
odm
oem
product
system_ext
Hợp nhất các tệp
Việc sử dụng thẻ <merge>
cho phép hợp nhất các tệp cấu hình khác tại vị trí được chỉ định vào tệp cấu hình. Thuộc tính path
của thẻ đại diện cho đường dẫn của tệp cần hợp nhất so với thư mục chứa các tệp cấu hình lớp phủ.
Sử dụng thuộc tính tệp kê khai/RRO tĩnh
Trên Android 10 trở xuống, tính chất không thể thay đổi và mức độ ưu tiên của lớp phủ được định cấu hình bằng các thuộc tính tệp kê khai sau.
android:isStatic
. Khi bạn đặt giá trị của thuộc tính boolean này thànhtrue
, lớp phủ sẽ được bật theo mặc định và không thể thay đổi, giúp lớp phủ không bị tắt.android:priority
. Giá trị của thuộc tính dạng số này (chỉ ảnh hưởng đến lớp phủ tĩnh) sẽ định cấu hình mức độ ưu tiên của lớp phủ khi nhiều lớp phủ tĩnh nhắm đến cùng một giá trị tài nguyên. Số càng cao thì mức độ ưu tiên càng cao.
Mã sau đây cho thấy một AndroidManifest.xml
mẫu.
<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>
Các thay đổi trong Android 11
Trong Android 11 trở lên, nếu tệp cấu hình nằm trong partition/overlay/config/config.xml
, thì các lớp phủ sẽ được định cấu hình bằng cách sử dụng tệp đó, đồng thời android:isStatic
và android:priority
sẽ không ảnh hưởng đến các lớp phủ trong phân vùng. Việc xác định tệp cấu hình lớp phủ trong bất kỳ phân vùng nào sẽ thực thi mức độ ưu tiên của phân vùng lớp phủ.
Ngoài ra, Android 11 trở lên sẽ xoá khả năng sử dụng lớp phủ tĩnh để ảnh hưởng đến các giá trị của tài nguyên được đọc trong quá trình cài đặt gói. Đối với trường hợp sử dụng phổ biến là sử dụng lớp phủ tĩnh để thay đổi giá trị của boolean định cấu hình trạng thái bật thành phần, hãy sử dụng thẻ <component-override>
SystemConfig
(mới trong Android 11).
Lớp phủ gỡ lỗi
Để bật, tắt và kết xuất lớp phủ theo cách thủ công, hãy sử dụng lệnh shell của trình quản lý lớp phủ sau.
adb shell cmd overlay
OverlayManagerService
sử dụng idmap2
để liên kết mã nhận dạng tài nguyên trong gói mục tiêu với mã nhận dạng tài nguyên trong gói lớp phủ. Các liên kết mã nhận dạng đã tạo được lưu trữ trong /data/resource-cache/
. Nếu lớp phủ của bạn không hoạt động đúng cách, hãy tìm tệp idmap
tương ứng cho lớp phủ của bạn trong /data/resource-cache/
, rồi chạy lệnh sau.
adb shell idmap2 dump --idmap-path [file]
Lệnh này sẽ in bản đồ ánh xạ tài nguyên như minh hoạ bên dưới.
[target res id] - > [overlay res id] [resource name]
0x01040151 -> 0x01050001 string/config_dozeComponent
0x01040152 -> 0x01050002 string/config_dozeDoubleTapSensorType
0x01040153 -> 0x01050003 string/config_dozeLongPressSensorType