การซ้อนทับทรัพยากรรันไทม์ (RRO) คือแพ็กเกจที่เปลี่ยนค่าทรัพยากรของแพ็กเกจเป้าหมายขณะรันไทม์ เช่น แอปที่ติดตั้งอยู่ในอิมเมจระบบอาจเปลี่ยนแปลงลักษณะการทำงานตามค่าของทรัพยากร แทนที่จะฮาร์ดโค้ดค่าทรัพยากรในเวลาบิลด์ RRO ที่ติดตั้งในพาร์ติชันอื่นจะเปลี่ยนค่าของทรัพยากรของแอปขณะรันไทม์ได้
คุณเปิดหรือปิดใช้ RRO ได้ คุณสามารถตั้งค่าสถานะเปิด/ปิดแบบเป็นโปรแกรมเพื่อสลับความสามารถของ RRO ในการเปลี่ยนค่าทรัพยากร ระบบจะปิดใช้ RRO โดยค่าเริ่มต้น (แต่ระบบจะเปิดใช้ RRO แบบคงที่โดยค่าเริ่มต้น)
ทรัพยากรการวางซ้อน
การวางซ้อนจะทำงานโดยการแมปทรัพยากรที่กำหนดไว้ในแพ็กเกจการวางซ้อนกับทรัพยากรที่กำหนดไว้ในแพ็กเกจเป้าหมาย เมื่อแอปพยายามแก้ไขค่าของทรัพยากรในแพ็กเกจเป้าหมาย ระบบจะแสดงค่าของทรัพยากรการวางซ้อนที่จับคู่กับทรัพยากรเป้าหมายแทน
ตั้งค่าไฟล์ Manifest
ระบบจะถือว่าแพ็กเกจเป็นแพ็กเกจ RRO หากมีแท็ก <overlay>
เป็นแท็กย่อยของแท็ก <manifest>
ค่าของแอตทริบิวต์
android:targetPackage
ที่ต้องระบุจะระบุชื่อแพ็กเกจที่ RRO ตั้งใจจะวางซ้อนค่าของแอตทริบิวต์
android:targetName
(ไม่บังคับ) จะระบุชื่อของชุดทรัพยากรย่อยที่วางซ้อนได้ของแพ็กเกจเป้าหมายที่ RRO ตั้งใจจะวางซ้อน หากเป้าหมายไม่ได้กำหนดชุดทรัพยากรที่ซ้อนทับได้ ก็ไม่ควรมีแอตทริบิวต์นี้
โค้ดต่อไปนี้แสดงตัวอย่างการวางซ้อน 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>
การวางซ้อนไม่สามารถซ้อนทับโค้ดได้ จึงไม่มีไฟล์ DEX นอกจากนี้ แอตทริบิวต์ android:hasCode
ของแท็ก <application
> ในไฟล์ Manifest ต้องตั้งค่าเป็น false
กำหนดแมปทรัพยากร
ใน Android 11 ขึ้นไป กลไกที่แนะนำสำหรับการกำหนดการแมปทรัพยากรแบบซ้อนทับคือการสร้างไฟล์ในไดเรกทอรี res/xml
ของแพ็กเกจการวางซ้อน แจกแจงทรัพยากรเป้าหมายที่ควรซ้อนทับและค่าแทนที่ จากนั้นตั้งค่าแอตทริบิวต์ android:resourcesMap
ของแท็กไฟล์ Manifest ของ <overlay>
เป็นข้อมูลอ้างอิงไฟล์การแมปทรัพยากร
โค้ดต่อไปนี้แสดงตัวอย่างไฟล์ 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>
โค้ดต่อไปนี้แสดงตัวอย่างไฟล์ Manifest การวางซ้อน
<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>
สร้างแพ็กเกจ
Android 11 ขึ้นไปรองรับกฎบิลด์ Soong สำหรับการวางซ้อนที่ป้องกันไม่ให้ Android Asset Packaging Tool 2 (AAPT2) พยายามลบล้างการกำหนดค่าทรัพยากรที่มีค่าเดียวกัน (--no-resource-deduping
) และไม่ให้นำทรัพยากรที่ไม่มีการกำหนดค่าเริ่มต้นออก (--no-resource-removal
) โค้ดต่อไปนี้แสดงตัวอย่างไฟล์ Android.bp
runtime_resource_overlay {
name: "ExampleOverlay",
sdk_version: "current",
}
แก้ปัญหาเกี่ยวกับทรัพยากร
หากทรัพยากรเป้าหมายหรือทรัพยากรการวางซ้อนมีการกําหนดการกําหนดค่าหลายรายการสําหรับทรัพยากรที่กําลังค้นหา รันไทม์ของทรัพยากรจะแสดงค่าของการกําหนดค่าที่ตรงกับการกําหนดค่าของอุปกรณ์มากที่สุด หากต้องการทราบว่าการกำหนดค่าใดเป็นการกำหนดค่าที่ตรงกันมากที่สุด ให้ผสานชุดของการกำหนดค่าทรัพยากรซ้อนทับเข้ากับชุดการกำหนดค่าทรัพยากรเป้าหมาย จากนั้นทำตามขั้นตอนการแก้ปัญหาทรัพยากรปกติ (โปรดดูรายละเอียดในวิธีที่ Android ค้นหาทรัพยากรที่ตรงกันมากที่สุด)
ตัวอย่างเช่น หากโฆษณาซ้อนทับกำหนดค่าสำหรับการกำหนดค่า drawable-en
และเป้าหมายกำหนดค่าสำหรับ drawable-en-port
เท่ากับว่า drawable-en-port
มีค่าที่ตรงกันมากกว่า ดังนั้นระบบจะเลือกค่า drawable-en-port
ของการกำหนดค่าเป้าหมายขณะรันไทม์ หากต้องการวางซ้อนการกําหนดค่า drawable-en
ทั้งหมด การซ้อนทับต้องกําหนดค่าสําหรับการกําหนดค่า drawable-en
แต่ละรายการที่เป้าหมายกําหนด
การวางซ้อนสามารถอ้างอิงทรัพยากรของตนเองได้ โดยมีลักษณะการทำงานที่แตกต่างกันระหว่างรุ่นต่างๆ ของ Android
ใน Android 11 ขึ้นไป การวางซ้อนแต่ละรายการจะมีพื้นที่รหัสทรัพยากรที่จองไว้ของตนเอง ซึ่งไม่ซ้อนทับกับพื้นที่รหัสทรัพยากรเป้าหมายหรือพื้นที่รหัสทรัพยากรของการวางซ้อนอื่นๆ ดังนั้นการวางซ้อนที่อ้างอิงทรัพยากรของตนเองจะทำงานตามที่คาดไว้
ใน Android 10 หรือต่ำกว่า การวางซ้อนและแพ็กเกจเป้าหมายจะใช้พื้นที่รหัสทรัพยากรเดียวกัน ซึ่งอาจทำให้เกิดข้อขัดแย้งและลักษณะการทำงานที่ไม่คาดคิดเมื่อพยายามอ้างอิงทรัพยากรของตนเองโดยใช้ไวยากรณ์
@type/name
เปิด/ปิดใช้การวางซ้อน
ใช้ OverlayManager
API เพื่อเปิดและปิดใช้การวางซ้อนที่เปลี่ยนแปลงได้ (ดึงข้อมูลอินเทอร์เฟซ API โดยใช้ Context#getSystemService(Context.OVERLAY_SERVICE)
) แพ็กเกจที่กำหนดเป้าหมายหรือแพ็กเกจที่มีสิทธิ์ android.permission.CHANGE_OVERLAY_PACKAGES
เท่านั้นที่เปิดใช้การวางซ้อนได้ เมื่อเปิดใช้หรือปิดใช้โฆษณาซ้อนทับ เหตุการณ์การเปลี่ยนแปลงการกำหนดค่าจะมีผลกับแพ็กเกจเป้าหมายและกิจกรรมเป้าหมายจะเปิดอีกครั้ง
จำกัดทรัพยากรที่วางซ้อนได้
ใน Android 10 ขึ้นไป แท็ก <overlayable>
XML จะแสดงชุดทรัพยากรที่ RRO วางซ้อนได้ ในไฟล์ res/values/overlayable.xml
ตัวอย่างต่อไปนี้ string/foo
และ integer/bar
เป็นทรัพยากรที่ใช้กำหนดธีมลักษณะที่ปรากฏของอุปกรณ์ หากต้องการวางซ้อนทรัพยากรเหล่านี้ โฆษณาซ้อนทับต้องกำหนดเป้าหมายคอลเล็กชันทรัพยากรที่ซ้อนทับได้ด้วยชื่ออย่างชัดแจ้ง
<!-- 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 กำหนดแท็ก <overlayable>
ได้หลายรายการ แต่แต่ละแท็กต้องมีชื่อที่ไม่ซ้ำกันภายในแพ็กเกจ เช่น
ตกลงสำหรับ 2 แพ็กเกจที่แตกต่างกันเพื่อกำหนด
<overlayable name="foo">
ทั้งคู่APK รายการเดียวต้องมีบล็อก
<overlayable name="foo">
2 บล็อก
โค้ดต่อไปนี้แสดงตัวอย่างการวางซ้อนใน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>
เมื่อแอปกําหนดแท็ก <overlayable>
การซ้อนทับที่กําหนดเป้าหมายแอปนั้น
ต้องระบุ
targetName
วางซ้อนได้เฉพาะทรัพยากรที่ระบุภายในแท็ก
<overlayable>
กําหนดเป้าหมายได้เพียงชื่อ
<overlayable>
รายการเดียว
คุณไม่สามารถเปิดใช้การวางซ้อนที่กำหนดเป้าหมายไปยังแพ็กเกจที่แสดงทรัพยากรที่วางซ้อนได้ แต่ไม่ได้ใช้ android:targetName
เพื่อกําหนดเป้าหมายแท็ก <overlayable>
ที่เฉพาะเจาะจง
จำกัดนโยบาย
ใช้แท็ก <policy>
เพื่อบังคับใช้ข้อจำกัดกับทรัพยากรที่วางซ้อนกันได้ แอตทริบิวต์ type
จะระบุนโยบายที่การวางซ้อนต้องเป็นไปตามข้อกำหนดเพื่อลบล้างทรัพยากรที่รวมไว้ ประเภทที่รองรับมีดังต่อไปนี้
public
การซ้อนทับใดๆ จะลบล้างทรัพยากรได้system
การซ้อนทับใดๆ ในพาร์ติชันระบบจะลบล้างทรัพยากรได้vendor
การวางซ้อนบนพาร์ติชันผู้ให้บริการลบล้างทรัพยากรได้product
การวางซ้อนบนพาร์ติชันผลิตภัณฑ์จะลบล้างทรัพยากรได้oem
. การซ้อนทับใดๆ ในพาร์ติชัน OEM จะลบล้างทรัพยากรได้odm
. การซ้อนทับใดๆ ในพาร์ติชัน odm จะลบล้างทรัพยากรได้signature
. การวางซ้อนที่ลงนามด้วยลายเซ็นเดียวกับ APK เป้าหมายจะลบล้างทรัพยากรได้actor
. การวางซ้อนที่ลงนามด้วยลายเซ็นเดียวกันกับ APK Actor สามารถลบล้างทรัพยากรได้ มีการประกาศผู้ดำเนินการในแท็ก named-actor ในการกำหนดค่าระบบconfig_signature
โฆษณาซ้อนทับที่ลงนามด้วยลายเซ็นเดียวกันกับ APK overlay-config อาจลบล้างทรัพยากรได้ การกำหนดค่าการวางซ้อนจะประกาศไว้ในแท็ก overlay-config-signature ในการกำหนดค่าของระบบ
โค้ดต่อไปนี้แสดงตัวอย่างแท็ก <policy>
ในไฟล์ 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>
หากต้องการระบุหลายนโยบาย ให้ใช้แถบแนวตั้ง (|) เป็นอักขระคั่น
เมื่อระบุนโยบายหลายรายการ โฆษณาซ้อนทับต้องเป็นไปตามนโยบายเพียงนโยบายเดียวเพื่อลบล้างทรัพยากรที่แสดงภายในแท็ก <policy>
กำหนดค่าการวางซ้อน
Android รองรับกลไกต่างๆ ในการกำหนดค่าความสามารถในการเปลี่ยนแปลง สถานะเริ่มต้น และลำดับความสำคัญของการวางซ้อน โดยขึ้นอยู่กับเวอร์ชันของ Android
อุปกรณ์ที่ใช้ Android 11 ขึ้นไปสามารถใช้ไฟล์
OverlayConfig
(config.xml
) แทนแอตทริบิวต์ไฟล์ Manifest ได้ เราขอแนะนำให้ใช้ไฟล์การวางซ้อนอุปกรณ์ทุกเครื่องสามารถใช้แอตทริบิวต์ไฟล์ Manifest (
android:isStatic
และandroid:priority
) เพื่อกำหนดค่า RRO แบบคงที่
ใช้ OverlayConfig
ใน Android 11 ขึ้นไป คุณสามารถใช้ OverlayConfig
เพื่อกำหนดค่าความสามารถในการเปลี่ยนแปลง สถานะเริ่มต้น และลำดับความสำคัญของการวางซ้อน หากต้องการกำหนดค่าการวางซ้อน ให้สร้างหรือแก้ไขไฟล์ที่อยู่ใน partition/overlay/config/config.xml
โดยที่ partition
คือพาร์ติชันของการวางซ้อนที่จะกำหนดค่า หากต้องการกำหนดค่า การวางซ้อนต้องอยู่ในไดเรกทอรี overlay/
ของพาร์ติชันที่มีการกําหนดค่าการวางซ้อน โค้ดต่อไปนี้แสดงตัวอย่าง 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>"
แท็ก <overlay>
ต้องมีแอตทริบิวต์ package
ที่ระบุว่ากำลังกำหนดค่าแพ็กเกจการวางซ้อนใด แอตทริบิวต์ enabled
(ไม่บังคับ) จะควบคุมว่าจะเปิดใช้การวางซ้อนโดยค่าเริ่มต้นหรือไม่ (ค่าเริ่มต้นคือ false
) แอตทริบิวต์ mutable
(ไม่บังคับ) จะควบคุมว่าจะเปลี่ยนการวางซ้อนได้หรือไม่ และสามารถเปลี่ยนสถานะ "เปิดใช้" โดยใช้โปรแกรมรันไทม์ได้หรือไม่ (ค่าเริ่มต้นคือ true
) การวางซ้อนที่ไม่ได้ระบุไว้ในไฟล์การกําหนดค่าจะเปลี่ยนได้และปิดอยู่โดยค่าเริ่มต้น
ลําดับความสําคัญของการวางซ้อน
เมื่อโฆษณาซ้อนทับหลายรายการลบล้างทรัพยากรเดียวกัน ลำดับของโฆษณาซ้อนทับจึงมีความสำคัญ การวางซ้อนมีลําดับความสําคัญสูงกว่าการวางซ้อนที่มีการกําหนดค่าก่อนการกําหนดค่าของตนเอง ลําดับความสําคัญของการวางซ้อนในพาร์ติชันต่างๆ (จากน้อยไปมาก) มีดังนี้
system
vendor
odm
oem
product
system_ext
ผสานไฟล์
การใช้แท็ก <merge>
ช่วยให้ผสานไฟล์การกําหนดค่าอื่นๆ ลงในไฟล์การกําหนดค่าที่ตําแหน่งระบุได้ แอตทริบิวต์ path
ของแท็กแสดงถึงเส้นทางของไฟล์ที่จะผสานซึ่งสัมพันธ์กับไดเรกทอรีที่มีไฟล์การกําหนดค่าการวางซ้อน
ใช้แอตทริบิวต์ไฟล์ Manifest/RRO แบบคงที่
ใน Android 10 หรือต่ำกว่า ความเปลี่ยนแปลงของการวางซ้อนและลำดับความสำคัญจะได้รับการกำหนดค่าโดยใช้แอตทริบิวต์ไฟล์ Manifest ต่อไปนี้
android:isStatic
เมื่อกำหนดค่าของแอตทริบิวต์บูลีนนี้เป็นtrue
โฆษณาซ้อนทับจะเปิดใช้โดยค่าเริ่มต้นและจะเปลี่ยนแปลงไม่ได้ ซึ่งทำให้โฆษณาซ้อนทับปิดใช้งานไม่ได้android:priority
ค่าของแอตทริบิวต์ตัวเลขนี้ (ซึ่งมีผลเฉพาะกับโฆษณาซ้อนทับแบบคงที่) จะกำหนดค่าลำดับความสำคัญของโฆษณาซ้อนทับเมื่อโฆษณาซ้อนทับแบบคงที่หลายรายการกำหนดเป้าหมายไปยังค่าทรัพยากรเดียวกัน จำนวนสูงกว่าหมายถึง ลำดับความสำคัญที่สูงกว่า
โค้ดต่อไปนี้แสดงตัวอย่าง 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>
การเปลี่ยนแปลงใน Android 11
ใน Android 11 ขึ้นไป หากไฟล์การกําหนดค่าอยู่ใน partition/overlay/config/config.xml
ระบบจะกําหนดค่าการวางซ้อนโดยใช้ไฟล์นั้น และ android:isStatic
และ android:priority
จะไม่มีผลกับการวางซ้อนที่อยู่ในพาร์ติชัน การกําหนดไฟล์การกําหนดค่าการวางซ้อนในพาร์ติชันใดก็ตามจะบังคับใช้ลําดับความสําคัญของพาร์ติชันการวางซ้อน
นอกจากนี้ Android 11 ขึ้นไปจะนําความสามารถในการใช้การวางซ้อนแบบคงที่เพื่อส่งผลต่อค่าของทรัพยากรที่อ่านระหว่างการติดตั้งแพ็กเกจออก สําหรับกรณีการใช้งานทั่วไปในการใช้การวางซ้อนแบบคงที่เพื่อเปลี่ยนค่าบูลีนที่กำหนดค่าสถานะเปิดใช้คอมโพเนนต์ ให้ใช้แท็ก <component-override>
SystemConfig
(ใหม่ใน Android 11)
แก้ไขข้อบกพร่องของการวางซ้อน
หากต้องการเปิดใช้ ปิดใช้ และถ่ายโอนข้อมูลการวางซ้อนด้วยตนเอง ให้ใช้คำสั่งเชลล์ของผู้จัดการการวางซ้อนต่อไปนี้
adb shell cmd overlay
OverlayManagerService
ใช้ idmap2
เพื่อจับคู่รหัสทรัพยากรในแพ็กเกจเป้าหมายกับรหัสทรัพยากรในแพ็กเกจการวางซ้อน การแมปรหัสที่สร้างขึ้นจะเก็บอยู่ใน /data/resource-cache/
หากการวางซ้อนไม่ทํางานอย่างถูกต้อง ให้ค้นหาไฟล์ idmap
ที่สอดคล้องกันสําหรับการวางซ้อนใน /data/resource-cache/
แล้วเรียกใช้คําสั่งต่อไปนี้
adb shell idmap2 dump --idmap-path [file]
คำสั่งนี้จะพิมพ์การแมปทรัพยากรดังที่แสดงด้านล่าง
[target res id] - > [overlay res id] [resource name]
0x01040151 -> 0x01050001 string/config_dozeComponent
0x01040152 -> 0x01050002 string/config_dozeDoubleTapSensorType
0x01040153 -> 0x01050003 string/config_dozeLongPressSensorType