การซ้อนทับทรัพยากรรันไทม์ (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
เปิด/ปิดใช้การวางซ้อน
คุณเปิด/ปิดใช้การวางซ้อนด้วยตนเองหรือใช้โปรแกรมก็ได้
ปิดหรือเปิดใช้การวางซ้อนด้วยตนเอง
หากต้องการเปิดใช้และยืนยัน RRO ด้วยตนเอง ให้เรียกใช้คำสั่งต่อไปนี้
adb shell cmd overlay enable --user current com.example.carrro
adb shell cmd overlay list --user current | grep -i com.example com.example.carrro
ซึ่งจะเปิดใช้ RRO สําหรับผู้ใช้ระบบ (userId = 0
) ที่เป็นเจ้าของ SystemUI
คำสั่งนี้จะไม่ส่งผลต่อแอปที่ผู้ใช้ที่ทำงานอยู่เบื้องหน้าเริ่มต้น (userId = 10
) หากต้องการเปิดใช้ RRO สำหรับผู้ใช้ที่ทำงานอยู่เบื้องหน้า ให้ใช้พารามิเตอร์ -–user 10
ดังนี้
adb shell cmd overlay enable --user 10 com.example.carrro
เปิดหรือปิดใช้การวางซ้อนแบบเป็นโปรแกรม
ใช้ 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 สามารถลบล้างทรัพยากรได้ ประกาศ Actor ในแท็ก named-actor ใน system configconfig_signature
. การวางซ้อนที่รับรองด้วยลายเซ็นเดียวกับ APK overlay-config จะลบล้างทรัพยากรได้ มีการประกาศ 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
การใช้ enable
โดยไม่ระบุผู้ใช้จะมีผลกับผู้ใช้ปัจจุบัน ซึ่งก็คือผู้ใช้ระบบ (userId = 0
) ที่เป็นเจ้าของ UI ของระบบ การดำเนินการนี้จะไม่ส่งผลต่อผู้ใช้ที่ทำงานอยู่เบื้องหน้า (userId = 10
) ซึ่งเป็นเจ้าของแอป หากต้องการเปิดใช้ RRO สําหรับผู้ใช้ที่ทำงานอยู่เบื้องหน้า ให้ใช้พารามิเตอร์ –-user 10
ดังนี้
adb shell cmd overlay enable --user 10 com.example.carrro
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