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