Google cam kết thúc đẩy công bằng chủng tộc cho Cộng đồng người da đen. Xem cách thực hiện.

Thay đổi giá trị tài nguyên của ứng dụng trong thời gian chạy

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Lớp phủ tài nguyên thời gian chạy (RRO) là 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 nó dựa trên giá trị của tài nguyên. Thay vì mã hóa cứng giá trị tài nguyên khi xây dựng, một RRO được cài đặt trên một phân vùng khác có thể thay đổi giá trị tài nguyên của ứng dụng khi chạy.

RRO có thể được bật hoặc tắt. Bạn có thể thiết lập trạng thái bật/tắt theo chương trình để chuyển đổi 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 được bật theo mặc định).

tài nguyên lớp phủ

Lớp phủ hoạt động bằng cách ánh xạ các tài nguyên được xác định trong gói lớp phủ tớ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 ánh xạ tới sẽ được trả về thay thế.

Thiết lập bảng kê khai

Một gói được coi là gói RRO nếu nó chứa <overlay> là con của <manifest> .

  • Giá trị của thuộc tính android:targetPackage được yêu cầu 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 tùy chọn chỉ định tên của tập con tài nguyên có thể xếp chồng của gói đích mà RRO dự định xếp chồng. Nếu mục tiêu không xác định tập hợp tài nguyên có thể xếp chồng lên nhau, thì thuộc tính này sẽ không xuất hiện.

Đoạn mã sau hiển thị một lớp phủ ví dụ 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

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 đích sẽ được phủ lên và các giá trị thay thế của chúng, sau đó đặt giá trị của thuộc tính android:resourcesMap của thẻ kê khai <overlay> để tham chiếu đến tệp ánh xạ tài nguyên.

Đoạn mã sau hiển thị một ví dụ về tệp 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>

Đoạn mã sau hiển thị một ví dụ về tệp kê khai lớp phủ.

<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>

Xây dựng gói

Android 11 trở lên hỗ trợ quy tắc xây dựng Soong cho các lớp phủ ngăn Công cụ đóng gói tài sản Android 2 (AAPT2) cố gắng khấu trừ cấu hình của các tài nguyên có cùng giá trị ( --no-resource-deduping ) và loại bỏ các tài nguyên không có cấu hình mặc định ( --no-resource-removal ). Đoạn mã sau hiển thị một tệp Android.bp mẫu.

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

Giải quyết tài nguyên

Nếu tài nguyên đích 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 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 tập hợp các cấu hình tài nguyên lớp phủ vào tập hợp các cấu hình tài nguyên đích, sau đó thực hiện theo quy trình phân giải tài nguyên thông thường (để biết chi tiết, hãy tham khảo 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à đích xác định giá trị cho drawable-en-port , thì drawable-en-port sẽ khớp tốt hơn nên giá trị của cấu hình đích drawable-en-port là được chọn trong thời gian chạy. Để phủ tất cả các cấu hình drawable-en , lớp phủ phải xác định một giá trị cho từng cấu hình drawable-en mà mục tiêu xác định.

Các lớp phủ có thể tham chiếu tài nguyên của riêng chú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ủ có không gian ID tài nguyên dành riêng không trùng lặp với không gian ID tài nguyên đích hoặc các không gian ID tài nguyên lớp phủ khác, vì vậy, các lớp phủ tham chiếu tài nguyên riêng của chúng hoạt động như mong đợi.

  • Trong Android 10 trở xuống, lớp phủ và gói đích chia sẻ cùng một không gian ID tài nguyên, điều này 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 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) ). Lớp phủ chỉ có thể được bật theo gói mà nó nhắm mục tiêu hoặc theo gói có quyền android.permission.CHANGE_OVERLAY_PACKAGES . Khi một lớp phủ được bật hoặc tắt, các sự kiện thay đổi cấu hình sẽ truyền tới 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ủ

Trong Android 10 trở lên, thẻ XML <overlayable> hiển thị một tập hợp các tài nguyên mà RRO được phép lớp phủ. Trong tệp res/values/overlayable.xml của ví dụ sau, string/foointeger/bar là các tài nguyên được sử dụng để tạo chủ đề cho giao diện của 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 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>

APK có thể xác định nhiều <overlayable> nhưng mỗi thẻ phải có một tên duy nhất trong gói. Ví dụ, đó là:

  • OK cho hai gói khác nhau để xác định cả hai <overlayable name="foo"> .

  • Không ổn khi một APK có hai khối <overlayable name="foo"> .

Đoạn mã sau hiển thị một ví dụ về 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 <overlayable> , các lớp phủ nhắm mục tiêu ứng dụng đó:

  • Phải chỉ định targetName .

  • Chỉ có thể che phủ các tài nguyên được liệt kê trong <overlayable> .

  • Chỉ có thể nhắm mục tiêu một tên <overlayable> .

Bạn không thể bật lớp phủ nhắm mục tiêu gói hiển thị tài nguyên có thể lớp phủ nhưng không sử dụng android:targetName để nhắm mục tiêu <overlayable> cụ thể.

chính sách hạn chế

Sử dụng <policy> để thực thi các hạn chế đối với các tài nguyên có thể xếp chồng lên nhau. Thuộc tính type chỉ định các chính sách mà lớp phủ phải thực hiện để ghi đè các tài nguyên được bao gồm. Các loại được hỗ trợ bao gồm các loại sau.

  • public . Bất kỳ lớp phủ nào cũng có thể ghi đè lên 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 đè 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 đè 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 đè tài nguyên.
  • oem . Bất kỳ lớp phủ nào trên phân vùng OEM đều có thể ghi đè tài nguyên.
  • odm . Bất kỳ lớp phủ nào trên phân vùng odm đều có thể ghi đè tài nguyên.
  • signature . Bất kỳ lớp phủ nào được ký có cùng chữ ký với APK mục tiêu đều có thể ghi đè tài nguyên.
  • actor . Bất kỳ lớp phủ nào được ký có cùng chữ ký với APK diễn viên đều có thể ghi đè tài nguyên. Diễn viên được khai báo trong thẻ tên-actor trong cấu hình hệ thống.
  • config_signature . Bất kỳ lớp phủ nào được ký có cùng chữ ký với gói ứng dụng cấu hình lớp phủ đều có thể ghi đè tài nguyên. Cấu hình lớp phủ được khai báo trong thẻ overlay-config-signature trong cấu hình hệ thống.

Đoạn mã sau hiển thị một <policy> ví dụ 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 các thanh dọc (|) làm ký tự phân cách. Khi nhiều chính sách được chỉ định, lớp phủ chỉ cần thực hiện một chính sách để ghi đè các tài nguyên được liệt kê trong <policy> .

Định cấu hình lớp phủ

Android hỗ trợ các cơ chế khác nhau để đị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ủ tùy 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ể 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ủ.

  • Tất cả các thiết bị đều có thể sử dụng thuộc tính tệp kê khai ( android:isStaticandroid:priority ) để định cấu hình RRO tĩnh.

Sử dụng OverlayConfig

Trong 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 ở partition/overlay/config/config.xml , trong đó partition là phân vùng của lớp phủ sẽ được định cấu hình. Để được 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 cấu hình. Đoạn mã sau hiển thị một ví dụ 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 tùy chọn 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 tùy chọn kiểm soát xem lớp phủ có thể thay đổi hay không và có thể thay đổi trạng thái đã bật của nó theo chương trình khi 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à bị tắt theo mặc định.

ưu tiên lớp phủ

Khi nhiều lớp phủ ghi đè lên 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 lớn hơn lớp phủ có cấu hình trước cấu hình của chính nó. Thứ tự ưu tiên của các lớp phủ trong các phân vùng khác nhau (từ ưu tiên thấp nhất đến lớn nhất) như sau.

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

Hợp nhất các tập tin

Việc sử dụng các <merge> cho phép hợp nhất các tệp cấu hình khác tại vị trí đã 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 so với thư mục chứa tệp cấu hình lớp phủ.

Sử dụng thuộc tính bảng kê khai (RRO tĩnh)

Trong Android 10 trở xuống, tính không thay đổi của lớp phủ và mức độ ưu tiên đượ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ành true , lớp phủ được bật theo mặc định và không thay đổi, điều này ngăn không cho lớp phủ bị tắt.

  • 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 nhiều lớp phủ tĩnh nhắm đến cùng một giá trị tài nguyên. Số cao hơn cho biết mức độ ưu tiên cao hơn.

Đoạn mã sau hiển thị một ví dụ 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>

Những 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 , lớp phủ được định cấu hình bằng tệp đó và android:isStaticandroid:priority không ảnh hưởng đến lớp phủ nằm 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 quyền ưu tiên của 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 để tác động đến 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 các phép toán luận định cấu hình trạng thái bật 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ệnh trình bao trình quản lý lớp phủ sau.

adb shell cmd overlay

OverlayManagerService sử dụng idmap2 để ánh xạ ID tài nguyên trong gói đích tới ID tài nguyên trong gói lớp phủ. Ánh xạ ID đã tạo đượ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ủ của bạn trong /data/resource-cache/ , sau đó chạy lệnh sau.

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

Lệnh này in ánh xạ tài nguyên như hình 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