เปลี่ยนค่าทรัพยากรของแอปขณะรันไทม์

การวางซ้อนทรัพยากรรันไทม์ (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 PackAGE 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)) CANNOT TRANSLATE สามารถเปิดใช้การวางซ้อนได้โดยแพ็กเกจที่กำหนดเป้าหมายหรือโดยแพ็กเกจที่มี สิทธิ์ android.permission.CHANGE_OVERLAY_PACKAGES เมื่อการซ้อนทับ เปิดหรือปิดใช้งาน เหตุการณ์การเปลี่ยนแปลงการกำหนดค่าจะเผยแพร่ไปยังแพ็กเกจเป้าหมาย และกิจกรรมเป้าหมายจะเปิดอีกครั้ง

จำกัดทรัพยากรที่วางซ้อนได้

ใน Android 10 ขึ้นไป แท็ก XML ของ <overlayable> จะแสดงชุดทรัพยากร ที่ 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 ของผู้ดำเนินการสามารถ ลบล้างทรัพยากร มีการประกาศผู้ดำเนินการในแท็ก 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)

แก้ไขข้อบกพร่องของการวางซ้อน

หากต้องการเปิดใช้ ปิดใช้ และดัมพ์การวางซ้อนด้วยตนเอง ให้ใช้การวางซ้อนต่อไปนี้ คำสั่ง Shell ของเครื่องมือจัดการ

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