โอเวอร์เลย์ทรัพยากรรันไทม์ (RRO)

โอเวอร์เลย์ทรัพยากรรันไทม์ (RRO) เป็นแพ็กเกจที่เปลี่ยนค่าทรัพยากรของแพ็กเกจเป้าหมายขณะรันไทม์ ตัวอย่างเช่น แอพที่ติดตั้งบนอิมเมจระบบอาจเปลี่ยนลักษณะการทำงานตามค่าของทรัพยากร แทนที่จะฮาร์ดโค้ดค่าทรัพยากร ณ เวลาสร้าง RRO ที่ติดตั้งบนพาร์ติชันอื่นสามารถเปลี่ยนค่าของทรัพยากรของแอปในขณะรันไทม์ได้

RRO สามารถเปิดหรือปิดได้ คุณสามารถตั้งค่าสถานะเปิด/ปิดใช้งานโดยทางโปรแกรมเพื่อสลับความสามารถของ RRO ในการเปลี่ยนค่าทรัพยากร RRO ถูกปิดใช้งานโดยค่าเริ่มต้น (อย่างไรก็ตาม RRO แบบคงที่ จะถูกเปิดใช้งานตามค่าเริ่มต้น)

ทรัพยากรที่ซ้อนทับ

โอเวอร์เลย์ทำงานโดยการแมปทรัพยากรที่กำหนดไว้ในแพ็กเกจโอเวอร์เลย์กับรีซอร์สที่กำหนดไว้ในแพ็กเกจเป้าหมาย เมื่อแอพพยายามแก้ไขค่าของทรัพยากรในแพ็คเกจเป้าหมาย ค่าของทรัพยากรซ้อนทับที่ทรัพยากรเป้าหมายถูกจับคู่จะถูกส่งกลับแทน

ตั้งค่ารายการ

แพ็คเกจถือเป็นแพ็คเกจ 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 แอตทริบิวต์ของแท็กรายการ <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 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 หรือสูงกว่า แท็ก 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> ได้หลายแท็ก แต่แต่ละแท็กต้องมีชื่อที่ไม่ซ้ำภายในแพ็คเกจ ตัวอย่างเช่น มันคือ:

  • ตกลงสำหรับสองแพ็คเกจที่แตกต่างกันเพื่อกำหนด <overlayable name="foo">

  • ไม่เหมาะสมสำหรับ APK เดียวที่จะมี <overlayable name="foo"> สองบล็อก

โค้ดต่อไปนี้แสดงตัวอย่างการซ้อนทับในไฟล์ 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 . โอเวอร์เลย์ใดๆ บนพาร์ติชันผลิตภัณฑ์สามารถแทนที่รีซอร์สได้
  • signature โอเวอร์เลย์ใดๆ ที่ลงนามด้วยลายเซ็นเดียวกันกับ APK เป้าหมายสามารถแทนที่ทรัพยากรได้

โค้ดต่อไปนี้แสดงตัวอย่างแท็ก <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 ) แทนแอตทริบิวต์รายการได้ การใช้ไฟล์โอเวอร์เลย์เป็นวิธีที่แนะนำสำหรับโอเวอร์เลย์

  • อุปกรณ์ทั้งหมดสามารถใช้แอตทริบิวต์รายการ ( 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 เป็นทางเลือกจะควบคุมว่าโอเวอร์เลย์เป็นแบบ mutable หรือไม่ และสามารถเปลี่ยนแปลงสถานะที่เปิดใช้งานโดยทางโปรแกรม ณ รันไทม์ (ค่าเริ่มต้นคือ true ) โอเวอร์เลย์ที่ไม่อยู่ในไฟล์คอนฟิกูเรชันสามารถเปลี่ยนแปลงได้และปิดใช้งานโดยค่าเริ่มต้น

ลำดับความสำคัญของโอเวอร์เลย์

เมื่อโอเวอร์เลย์หลายรายการแทนที่ทรัพยากรเดียวกัน ลำดับของการซ้อนทับจึงมีความสำคัญ โอเวอร์เลย์มีความสำคัญมากกว่าโอเวอร์เลย์ที่มีคอนฟิกูเรชันก่อนหน้าคอนฟิกูเรชันของตัวเอง ลำดับความสำคัญของการซ้อนทับในพาร์ติชั่นต่างๆ (จากลำดับความสำคัญน้อยไปมาก) มีดังนี้

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

การรวมไฟล์

การใช้แท็ก <merge> ช่วยให้สามารถรวมไฟล์การกำหนดค่าอื่นๆ ที่ตำแหน่งที่ระบุลงในไฟล์การกำหนดค่าได้ แอตทริบิวต์ path ของแท็กแสดงถึงเส้นทางของไฟล์ที่จะผสานสัมพันธ์กับไดเร็กทอรีที่มีไฟล์คอนฟิกูเรชันโอเวอร์เลย์

การใช้แอตทริบิวต์รายการ (RRO แบบคงที่)

ใน Android 10 หรือต่ำกว่า ความไม่เปลี่ยนรูปแบบและลำดับความสำคัญของโอเวอร์เลย์ได้รับการกำหนดค่าโดยใช้แอตทริบิวต์รายการต่อไปนี้

  • แอนด 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 เพื่อจับคู่รหัสทรัพยากรในแพ็คเกจเป้าหมายกับรหัสทรัพยากรในแพ็คเกจโอเวอร์เลย์ การแมป ID ที่สร้างขึ้นจะถูกเก็บไว้ใน /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