ในอดีต Android รองรับเฉพาะหน้าหน่วยความจำขนาด 4 KB ซึ่งได้ เพิ่มประสิทธิภาพหน่วยความจำของระบบสำหรับปริมาณหน่วยความจำทั้งหมดโดยเฉลี่ยที่ อุปกรณ์ Android มักจะมี ตั้งแต่ Android 15 เป็นต้นไป AOSP จะรองรับ อุปกรณ์ที่กำหนดค่าให้ใช้หน้าขนาด 16 KB (อุปกรณ์ 16 KB) หากแอปใช้ไลบรารี NDK ไม่ว่าจะโดยตรงหรือโดยอ้อมผ่าน SDK คุณจะต้องสร้างแอปใหม่เพื่อให้ทำงานในอุปกรณ์ที่มีขนาด 16 KB เหล่านี้ได้
เนื่องจากผู้ผลิตอุปกรณ์ยังคงสร้างอุปกรณ์ที่มีหน่วยความจำจริง (RAM) มากขึ้น อุปกรณ์จำนวนมากเหล่านี้จะใช้ขนาดหน้า 16 KB (และในที่สุดก็จะใหญ่ขึ้น) เพื่อเพิ่มประสิทธิภาพของอุปกรณ์ การเพิ่มการรองรับอุปกรณ์ที่มีขนาดหน้า 16 KB จะช่วยให้แอปของคุณทำงานบนอุปกรณ์เหล่านี้ได้ และช่วยให้แอปได้รับประโยชน์จากการปรับปรุงประสิทธิภาพที่เกี่ยวข้อง หากไม่มีการคอมไพล์ใหม่ แอปจะใช้ไม่ได้ในอุปกรณ์แบบ 16 KB ใน Android รุ่นต่อๆ ไป
เราได้ให้คำแนะนำเกี่ยวกับวิธีตรวจสอบว่าแอปได้รับผลกระทบหรือไม่ วิธีสร้างแอปใหม่ (หากมี) และวิธีทดสอบแอปในสภาพแวดล้อมแบบ 16 KB โดยใช้โปรแกรมจำลอง (รวมถึงอิมเมจระบบ Android 15 สำหรับ Android Emulator) เพื่อช่วยให้คุณเพิ่มการรองรับแอปได้
ข้อกำหนดด้านความเข้ากันได้ของ Google Play
แอปทั้งหมดที่กำหนดเป้าหมายเป็น Android 15 (ระดับ API 35) ขึ้นไปต้องรองรับหน้าหน่วยความจำขนาด 16 KB ในอุปกรณ์ 64 บิตบน Google Play เพื่อให้มั่นใจว่าแอปของคุณจะทำงานได้อย่างถูกต้องใน Android เวอร์ชันล่าสุด ตั้งแต่วันที่ 1 กุมภาพันธ์ 2027 เป็นต้นไป หากการอัปเดตแอปของคุณไม่รองรับหน้าหน่วยความจำขนาด 16 KB คุณจะเผยแพร่การอัปเดตเหล่านี้ไม่ได้
ประโยชน์และประสิทธิภาพที่เพิ่มขึ้น
อุปกรณ์ที่กำหนดค่าหน้าเว็บขนาด 16 KB จะใช้หน่วยความจำโดยเฉลี่ยมากกว่าเล็กน้อย แต่ปรับปรุงประสิทธิภาพการทำงานได้หลากหลายสำหรับทั้งระบบและแอป ดังนี้
- เวลาเปิดแอปลดลงขณะที่ระบบมีภาระเกี่ยวกับหน่วยความจำ: ลดลงโดยเฉลี่ย 3.16% โดยการปรับปรุงที่เห็นได้ชัดมากขึ้น (สูงสุด 30%) สำหรับบางแอปที่เราทดสอบ
- การลดพลังงานในระหว่างการเปิดแอป: ลดลง 4.56% โดยเฉลี่ย
- การเปิดตัวกล้องเร็วขึ้น: Hot Start เร็วขึ้น 4.48% โดยเฉลี่ย และ Cold Start เร็วขึ้น 6.60% โดยเฉลี่ย
- ปรับปรุงเวลาในการบูตระบบ: เร็วขึ้น 8% (ประมาณ 950 มิลลิวินาที) โดยเฉลี่ย
การปรับปรุงเหล่านี้อิงตามการทดสอบขั้นต้นของเรา และผลลัพธ์ในอุปกรณ์จริงอาจแตกต่างกัน เราจะให้การวิเคราะห์เพิ่มเติมเกี่ยวกับโอกาสในการได้ผู้ใช้ใหม่สำหรับแอปขณะที่เราทำการทดสอบต่อไป
ตรวจสอบว่าแอปได้รับผลกระทบหรือไม่
หากแอปใช้โค้ดเนทีฟ คุณควรสร้างแอปใหม่ ให้รองรับอุปกรณ์ขนาด 16 KB หากไม่แน่ใจว่าแอปใช้โค้ดเนทีฟหรือไม่ คุณสามารถใช้ APK Analyzer เพื่อระบุว่ามีโค้ดเนทีฟหรือไม่ จากนั้นตรวจสอบการจัดแนวของส่วน ELF สำหรับไลบรารีที่ใช้ร่วมกันที่คุณพบ Android Studio ยังมีฟีเจอร์ที่จะช่วยให้คุณตรวจหาปัญหาการจัดแนวโดยอัตโนมัติได้ด้วย
หากแอปใช้เฉพาะโค้ดที่เขียนในภาษาโปรแกรม Java หรือ Kotlin ซึ่งรวมถึงไลบรารีหรือ SDK ทั้งหมด แสดงว่าแอปของคุณรองรับอุปกรณ์ขนาด 16 KB อยู่แล้ว อย่างไรก็ตาม เราขอแนะนำให้คุณทดสอบแอปในสภาพแวดล้อมแบบ 16 KB เพื่อยืนยันว่าไม่มีการถดถอยที่ไม่คาดคิดในลักษณะการทำงานของแอป
แอปของคุณใช้โค้ดแบบเนทีฟหรือไม่
แอปของคุณใช้โค้ดแบบเนทีฟหากมีข้อใดต่อไปนี้
- แอปของคุณใช้โค้ด C/C++ (เนทีฟ) หากแอปใช้ Android NDK แสดงว่าแอปใช้โค้ดแบบเนทีฟ
- แอปของคุณลิงก์กับไลบรารีแบบเนทีฟหรือทรัพยากร Dependency ของบุคคลที่สาม (เช่น SDK) ที่ใช้ไลบรารีหรือทรัพยากร Dependency ดังกล่าว
- แอปของคุณสร้างขึ้นโดยเครื่องมือสร้างแอปของบุคคลที่สามซึ่งใช้ไลบรารีแบบเนทีฟในอุปกรณ์
ระบุไลบรารีแบบเนทีฟโดยใช้ตัววิเคราะห์ APK
ตัววิเคราะห์ APK เป็นเครื่องมือที่ช่วยให้คุณประเมินแง่มุมต่างๆ ของ APK ที่สร้างขึ้นได้ วิธีตรวจสอบว่าแอปของคุณใช้โค้ดแบบเนทีฟหรือไม่ (ไม่ว่าแอปจะรองรับขนาดหน้า 16 KB หรือไม่ก็ตาม)
- เปิด Android Studio แล้วคลิกไฟล์ > เปิด แล้วเลือกโปรเจ็กต์
จากแถบเมนู ให้คลิก Build > Analyze APK...
เลือก APK ที่ต้องการวิเคราะห์
มองหาในโฟลเดอร์
libซึ่งโฮสต์ไฟล์ออบเจ็กต์ที่แชร์ (.so) หากมี หากมีไฟล์ออบเจ็กต์ที่แชร์อยู่ แสดงว่าแอปใช้โค้ดแบบเนทีฟ คอลัมน์การจัดแนวจะแสดงข้อความเตือนสำหรับไฟล์ที่มีปัญหาเกี่ยวกับการจัดแนว หากไม่มีไฟล์ออบเจ็กต์ที่ใช้ร่วมกันหรือไม่มีโฟลเดอร์libแสดงว่าแอปของคุณไม่ได้ใช้โค้ดแบบเนทีฟ
ตรวจหาปัญหาการจัดแนวด้วยการตรวจสอบอัตโนมัติ
Android Studio จะเตือนคุณล่วงหน้าหากไลบรารีหรือ APK ที่สร้างไว้ล่วงหน้าไม่เป็นไปตามข้อกำหนด 16 KB ใช้เครื่องมือตัววิเคราะห์ APK เพื่อตรวจสอบว่าไลบรารีใดที่ต้องอัปเดตหรือต้องทำการเปลี่ยนแปลงโค้ดหรือไม่
เครื่องมือวิเคราะห์ซอร์สโค้ดใน Android Studio ยังไฮไลต์ไลบรารีแบบเนทีฟที่ไม่ได้จัดเรียงตามรูปแบบความจำขนาด 16 KB ด้วย
ตรวจสอบการจัดแนวของกลุ่ม ELF สำหรับคลังที่ใช้ร่วมกัน
สำหรับคลังที่ใช้ร่วมกัน ให้ตรวจสอบว่าส่วน ELF ของคลังที่ใช้ร่วมกัน ได้รับการจัดแนวอย่างถูกต้องโดยใช้การจัดแนว ELF ขนาด 16 KB หากคุณพัฒนาบน Linux หรือ macOS คุณสามารถใช้สคริปต์ check_elf_alignment.sh ตามที่อธิบายไว้ในส่วนต่อไปนี้
นอกจากนี้ คุณยังใช้เครื่องมือบรรทัดคำสั่งได้โดยตรงด้วย
ใช้สคริปต์ check_elf_alignment.sh (Linux หรือ macOS)
ทำตามขั้นตอนต่อไปนี้เพื่อตรวจสอบการจัดแนวของเซกเมนต์ ELF โดยใช้สคริปต์ check_elf_alignment.sh
บันทึกสคริปต์
check_elf_alignment.shลงในไฟล์เรียกใช้สคริปต์ในไฟล์ APK ของแอปโดยทำดังนี้
check_elf_alignment.sh APK_NAME.apkสคริปต์จะแสดงผลเป็น
ALIGNEDหรือUNALIGNEDสำหรับคลังที่ใช้ร่วมกันarm64-v8aทั้งหมดหากไลบรารีที่ใช้ร่วมกัน
arm64-v8aหรือx86_64เป็นUNALIGNEDคุณจะต้องอัปเดตแพ็กเกจสำหรับไลบรารีเหล่านั้น จากนั้นคอมไพล์แอปใหม่และทดสอบอีกครั้งโดยทำตามขั้นตอนในส่วนนี้
ใช้เครื่องมือบรรทัดคำสั่งโดยตรง
ทำตามขั้นตอนต่อไปนี้เพื่อตรวจสอบการจัดแนวของกลุ่ม ELF โดยใช้เครื่องมือบรรทัดคำสั่งโดยตรง
- ตรวจสอบว่าได้ติดตั้งทั้ง Android SDK Build-Tools เวอร์ชัน 35.0.0 ขึ้นไปและ Android NDK โดยใช้เครื่องมือจัดการ SDK ใน Android Studio หรือเครื่องมือบรรทัดคำสั่ง
sdkmanager แยกไฟล์ APK ของแอปโดยทำดังนี้
Linux หรือ macOS
unzip APK_NAME.apk -d /tmp/my_apk_outWindows (PowerShell)
Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_outในไดเรกทอรีชั่วคราวที่คุณแตกไฟล์ APK ให้ตรวจสอบเนื้อหาของไดเรกทอรี
libเพื่อหาไฟล์ออบเจ็กต์ที่ใช้ร่วมกัน (.so) ไฟล์เหล่านี้เป็นไฟล์ออบเจ็กต์ที่ใช้ร่วมกันแบบเดียวกับที่คุณเห็นขณะระบุไลบรารีแบบเนทีฟโดยใช้ตัววิเคราะห์ APK เรียกใช้คำสั่งต่อไปนี้ในแต่ละ ไฟล์ออบเจ็กต์ที่ใช้ร่วมกันLinux หรือ macOS
SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p SHARED_OBJECT_FILE.so | grep LOADWindows (PowerShell)
SDK_ROOT_LOCATION\Android\sdk\ndk\NDK_VERSION\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-objdump.exe -p SHARED_OBJECT_FILE.so | Select-String -Pattern "LOAD"โดย
SDK_ROOT_LOCATIONคือเส้นทางไปยังไดเรกทอรีที่คุณติดตั้ง Android SDK,SHARED_OBJECT_FILEคือชื่อของไฟล์ออบเจ็กต์ที่ใช้ร่วมกันที่คุณกำลังตรวจสอบ และNDK_VERSIONคือเวอร์ชันของ Android NDK ที่คุณติดตั้ง (เช่น28.0.12433566) เอาต์พุตจะมีลักษณะดังต่อไปนี้สำหรับแต่ละไฟล์ที่คุณตรวจสอบLOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**14 LOAD off 0x0000000000042a90 vaddr 0x0000000000043a90 paddr 0x0000000000043a90 align 2**14 LOAD off 0x0000000000046230 vaddr 0x0000000000048230 paddr 0x0000000000048230 align 2**14ตรวจสอบบรรทัดเอาต์พุตเพื่อให้แน่ใจว่ากลุ่มโหลดไม่มีค่าต่ำกว่า
2**14หากส่วนการโหลดมีค่า2**13,2**12หรือต่ำกว่า คุณจะต้องอัปเดตการสร้างแพ็กเกจสำหรับไลบรารีเหล่านั้น จากนั้นรีคอมไพล์แอปและทดสอบอีกครั้งโดยทำตามขั้นตอนในส่วนนี้ถัดไป ให้เรียกใช้เครื่องมือบรรทัดคำสั่ง
zipalignในไฟล์ APK ของแอปLinux หรือ macOS
SDK_ROOT_LOCATION/Android/sdk/build-tools/35.0.0/zipalign -v -c -P 16 4 APK_NAME.apkWindows (PowerShell)
SDK_ROOT_LOCATION\Android\sdk\build-tools\35.0.0\zipalign.exe -v -c -P 16 4 APK_NAME.apkโดย
SDK_ROOT_LOCATIONคือเส้นทางไปยังไดเรกทอรีที่คุณติดตั้ง Android SDK และAPK_NAMEคือชื่อไฟล์ APK ของแอป บรรทัดสุดท้ายของเอาต์พุตจะระบุว่า "ยืนยันสำเร็จ" หากคลังที่ใช้ร่วมกันทั้งหมดสอดคล้องกันอย่างถูกต้องหากการยืนยันไม่สำเร็จ คุณจะต้องจัดเรียงไลบรารีที่ใช้ร่วมกันบางรายการใหม่ โดยอัปเดตแพ็กเกจสำหรับไลบรารีเหล่านั้น จากนั้นคอมไพล์แอปใหม่และทดสอบอีกครั้งโดยทำตามขั้นตอนในส่วนนี้
ตรวจสอบแฟล็กความปลอดภัยของ RELRO
เพื่อลดการใช้ประโยชน์จากช่องโหว่ด้านความปลอดภัย Linker สมัยใหม่จะใช้แฟล็ก Relocation Read-Only (RELRO) เพื่อทำให้ส่วนการย้ายตำแหน่งของไฟล์ออบเจ็กต์ที่แชร์เป็นแบบอ่านอย่างเดียวหลังจากโหลด เปิดใช้แฟล็ก RELRO ในบิลด์
ส่วน RELRO ที่มีที่อยู่เริ่มต้นและขนาดกลุ่ม (MemSize) ที่ไม่ได้
จัดแนว 16 KB จะทําให้แอปขัดข้องขณะรันไทม์เนื่องจากข้อผิดพลาดในการแบ่งส่วน กรณีนี้จะเกิดขึ้นหากไฟล์ .so สร้างขึ้นด้วยเชนเครื่องมือ NDK r27 และต่ำกว่าโดยไม่ได้เปิดใช้แฟล็กที่เกี่ยวข้อง
เรียกใช้คำสั่งต่อไปนี้ในไฟล์ออบเจ็กต์ที่แชร์แต่ละไฟล์ (Linux หรือ macOS)
SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-readelf -Wl SHARED_OBJECT_FILE.so | grep 'RELRO\|Type'
ระบบจะพิมพ์สตริง GNU_RELRO หากมีกลุ่ม RELRO
จากนั้นตรวจสอบการจัดแนวส่วน RELRO โดยรวมที่อยู่ชดเชยเสมือน (VirtAddr) กับขนาดหน่วยความจำของส่วน (MemSiz) และหารด้วย 16 KB (0x4000) หากเศษ (โมดูโล) เป็น 0 แสดงว่ากลุ่ม RELRO มีการจัดเรียงขนาด 16 KB
ตัวอย่างไฟล์ .so ที่ไม่ได้จัดแนว
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x01510 R 0x1
สูตร: (VirtAddr + MemSiz) % 0x4000 == 0
ผลลัพธ์: (0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000
เนื่องจาก 0x1000 ไม่ใช่ 0 ไฟล์ .so นี้จึงไม่เป็นไปตามข้อกำหนด 16 KB ช่วงการป้องกัน RELRO
จาก DC000 (ตัวแบ่งหน้าก่อนหน้า) ถึง E4000 เป็นแบบอ่านอย่างเดียว
แต่ Android Linker คาดหวังให้ช่วงย่อยจาก E1000 ถึง E4000 เป็นแบบ
เขียนได้ ซึ่งส่งผลให้เกิดข้อผิดพลาดในการแบ่งส่วน ในกรณีนี้ ให้สร้างไฟล์ .so อีกครั้งตามที่อธิบายไว้ในส่วนคอมไพล์แอปโดยใช้การจัดวาง ELF ขนาด 16 KB
ตัวอย่างไฟล์ .so ที่จัดแนวแล้วพร้อมด้วยส่วน RELRO มีดังนี้
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x00510 R 0x1
ตัวอย่างไฟล์ .so ที่ไม่มีกลุ่ม RELRO มีดังนี้
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
นี่คือไฟล์ .so ที่เป็นไปตามข้อกำหนดขนาด 16 KB โดยไม่มีกลุ่ม RELRO
ต่อไปนี้คือตัวอย่างไฟล์ .so ที่จัดแนวซึ่ง GNU_RELRO อยู่ใน
LOAD ของตัวเอง
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000680 0x000680 R E 0x4000
LOAD 0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 RW 0x4000
GNU_RELRO 0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 R 0x1
สูตร: (VirtAddr + MemSiz) % 0x4000 == 0
ผลลัพธ์: (0x4680 + 0x0980) % 0x4000 = 0x5000 % 0x4000 == 0x1000
แม้ว่า 0x1000 จะไม่ใช่ 0 แต่ไฟล์ .so นี้ก็ยังเป็นไปตามข้อกำหนด 16 KB
สูตรโมดูโลจะใช้เมื่อ GNU_RELRO แชร์กลุ่ม LOAD กับข้อมูลที่เขียนได้ที่ตามมา (เช่น .data หรือ .bss)
หากต้องการตรวจสอบว่า GNU_RELRO อยู่ในกลุ่ม LOAD ของตัวเองหรือไม่ ให้ตรวจสอบส่วนหัวของโปรแกรมโดยเรียกใช้ llvm-readelf -Wl และกรองด้วย grep 'LOAD\|RELRO\|Type' เปรียบเทียบ VirtAddr และ MemSiz ของ GNU_RELRO กับกลุ่ม LOAD ที่มีอยู่ ในตัวอย่างนี้ ทั้ง 2 รายการมี VirtAddr 0x4680 และ MemSiz 0x0980
เนื่องจากทั้งส่วน LOAD มีเฉพาะข้อมูล RELRO จึงไม่มีข้อมูลที่เขียนได้ในช่วงย่อยจาก 0x5000 ถึง 0x8000 นอกจากนี้ ส่วน LOAD ที่เขียนได้ที่ตามมาจะเริ่มต้นที่ขอบเขตหน้าขนาด 16 KB แยกต่างหาก (ที่ 0x8000 ขึ้นไป) การป้องกันแบบอ่านอย่างเดียวของ Linker จะไม่
ทับซ้อนกับข้อมูลที่เขียนได้ จึงไม่เกิดข้อผิดพลาดในการแบ่งส่วน
สร้างแอปให้รองรับอุปกรณ์ที่มีหน้าหน่วยความจำขนาด 16 KB
หากแอปใช้โค้ดแบบเนทีฟ ให้ทําตามขั้นตอนที่ระบุไว้ในส่วนต่อไปนี้เพื่อให้แน่ใจว่าแอปของคุณรองรับอุปกรณ์ขนาด 16 KB
- อัปเดตการสร้างแพ็กเกจของคลังที่ใช้ร่วมกัน
- คอมไพล์แอปโดยใช้การจัดวาง ELF ขนาด 16 KB
- แก้ไขโค้ดและแก้ปัญหาเกี่ยวกับรันไทม์
- เพิ่มประสิทธิภาพเครื่องมือจัดสรรหน่วยความจำที่กำหนดเอง (หากมี)
- ตรวจสอบ SDK เพื่อดูการรองรับขนาด 16 KB
อัปเดตการสร้างแพ็กเกจของคลังที่ใช้ร่วมกัน
อัปเกรดเป็น AGP เวอร์ชัน 8.5.1 ขึ้นไปและใช้ไลบรารีที่ใช้ร่วมกันที่ไม่ได้บีบอัด
ใช้ bundletool เพื่อยืนยันการจัดแนวซิป
หากต้องการดูการจัดแนวของแพ็กเกจ ให้ใช้คำสั่งต่อไปนี้
bundletool dump config --bundle=<my .aab> | grep alignment
หากเห็น PAGE_ALIGNMENT_16K แสดงว่า Bundle ของคุณขอการจัดแนวไฟล์ ZIP ขนาด 16 KB หากเห็น PAGE_ALIGNMENT_4K แสดงว่า APK ที่สร้างจาก AAB นี้มีไฟล์ .so ที่จัดแนว 4 KB ในไฟล์ ZIP
AGP เวอร์ชัน 8.5.1 ขึ้นไป
อุปกรณ์ 16 KB กำหนดให้แอปที่จัดส่งพร้อมคลังที่ใช้ร่วมกันที่ไม่ได้บีบอัดต้องจัดวางคลังที่ใช้ร่วมกันเหล่านั้นในขอบเขตที่จัดวางด้วย zip ขนาด 16 KB ซึ่งคุณจะต้องอัปเกรดเป็นปลั๊กอิน Android Gradle (AGP) เวอร์ชัน 8.5.1 ขึ้นไป ดูรายละเอียดเกี่ยวกับกระบวนการอัปเกรดได้ที่ส่วนผู้ช่วยอัปเกรดปลั๊กอิน Android Gradle
AGP เวอร์ชัน 8.5 หรือต่ำกว่า
หากอัปเกรด AGP เป็นเวอร์ชัน 8.5.1 ขึ้นไปไม่ได้ คุณสามารถเปลี่ยนไปใช้คลังที่ใช้ร่วมกันแบบบีบอัดแทนได้ อัปเดตการกำหนดค่า Gradle เพื่อให้ Gradle บีบอัดคลังที่ใช้ร่วมกันเมื่อสร้างแพ็กเกจแอปเพื่อหลีกเลี่ยงปัญหาการติดตั้งแอปที่มีคลังที่ใช้ร่วมกันที่ไม่ได้จัดวาง
ดึงดูด
เพิ่มตัวเลือกต่อไปนี้ในไฟล์ build.gradle
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}
Kotlin
เพิ่มตัวเลือกต่อไปนี้ในไฟล์ build.gradle.kts
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
}
AGP เวอร์ชัน 8.0 หรือต่ำกว่า
หากใช้ AGP เวอร์ชัน 8.0 หรือต่ำกว่า คุณจะต้องปิดใช้ตัวเลือกไลบรารีแบบเนทีฟที่ไม่ได้บีบอัดสำหรับ App Bundle ในไฟล์ gradle.properties ด้วย
android.bundle.enableUncompressedNativeLibs=false
คอมไพล์แอปโดยใช้การจัดวาง ELF ขนาด 16 KB
อุปกรณ์ขนาด 16 KB กำหนดให้ต้องจัดวางเซกเมนต์ ELF ของคลังที่ใช้ร่วมกันอย่างเหมาะสมโดยใช้การจัดวาง ELF ขนาด 16 KB เพื่อให้แอปทำงานได้
สำหรับนักพัฒนาเกม หากเกมของคุณทำงานบนเกมเอนจิน Unity โปรดดูคู่มือ Unity หากเกมของคุณทำงานบนเกมเอนจิน Unreal โปรดดูคำแนะนำสำหรับ Unreal สำหรับเกมเอนจินเนทีฟ ให้ทำตามคำแนะนำนี้ต่อไป
หากต้องการคอมไพล์แอปโดยใช้การจัดแนว ELF ขนาด 16 KB ให้ทำตามขั้นตอนในส่วนใดส่วนหนึ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับเวอร์ชันของ Android NDK ที่คุณใช้
Android NDK r28 ขึ้นไป
NDK เวอร์ชัน r28 ขึ้นไปจะคอมไพล์การจัดแนว 16 KB โดยค่าเริ่มต้น
Android NDK r27 และต่ำกว่า
หากต้องการรองรับการคอมไพล์คลังที่ใช้ร่วมกันซึ่งจัดแนว 16 KB ด้วย Android NDK เวอร์ชัน r27 หรือต่ำกว่า ให้ใช้แฟลกลิงก์เกอร์ต่อไปนี้
-Wl,-z,max-page-size=16384
-Wl,-z,common-page-size=16384
วิธีอัปเดตไฟล์การกำหนดค่าระบบบิลด์มีดังนี้
ndk-build
หากคุณใช้ ndk-build ให้อัปเดต Android.mk เพื่อเปิดใช้การจัดแนว ELF ขนาด 16 KB
ดังนี้
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384
CMake
หากใช้ CMake ให้อัปเดต CMakeLists.txt เพื่อเปิดใช้การจัดแนว ELF ขนาด 16 KB
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
"-Wl,-z,max-page-size=16384"
"-Wl,-z,common-page-size=16384"
)
แก้ไขโค้ดและแก้ปัญหาเกี่ยวกับรันไทม์
แม้ว่าแอปจะจัดแนวขนาด 16 KB แล้ว แต่แอปอาจพบข้อผิดพลาดหากโค้ดบางส่วนคาดว่าอุปกรณ์ใช้ขนาดหน้าเว็บที่เฉพาะเจาะจง โปรดทำตามขั้นตอนต่อไปนี้เพื่อหลีกเลี่ยงปัญหานี้
นำทรัพยากร Dependency ที่ฮาร์ดโค้ดซึ่งอ้างอิงค่าคงที่
PAGE_SIZEหรืออินสแตนซ์ในตรรกะโค้ดที่ถือว่าขนาดหน้าเว็บของอุปกรณ์คือ 4 KB (4096) ออกโปรดใช้
getpagesize()หรือsysconf(_SC_PAGESIZE)แทนมองหาการใช้งาน
mmap()และ API อื่นๆ ที่ต้องมีอาร์กิวเมนต์ที่สอดคล้องกับหน้าเว็บ และแทนที่ด้วยทางเลือกอื่นหากจำเป็น
ในบางกรณี หากแอปใช้ PAGE_SIZE เป็นค่าที่สะดวกซึ่งไม่ได้เชื่อมโยงกับขนาดหน้าเว็บที่สำคัญ การดำเนินการนี้จะไม่ทำให้แอปขัดข้องเมื่อใช้ในโหมด 16 KB อย่างไรก็ตาม หากส่งค่านี้ไปยังเคอร์เนลด้วย mmap โดยไม่มี MAP_FIXED เคอร์เนลจะยังคงใช้ทั้งหน้า ซึ่งทำให้เสียหน่วยความจำบางส่วน ด้วยเหตุนี้ PAGE_SIZE จึงไม่ได้รับการกำหนดเมื่อเปิดใช้โหมด 16 KB ใน NDK r27 ขึ้นไป
หากแอปของคุณใช้ PAGE_SIZE ในลักษณะนี้และไม่เคยส่งค่านี้ไปยังเคอร์เนลโดยตรง ให้สร้างตัวแปรใหม่ที่มีชื่อใหม่เพื่อแสดงว่าตัวแปรนั้นใช้เพื่อวัตถุประสงค์อื่นและไม่ได้แสดงหน้าหน่วยความจำจริง แทนที่จะใช้ PAGE_SIZE
เพิ่มประสิทธิภาพเครื่องมือจัดสรรหน่วยความจำที่กำหนดเอง
ในระบบ 16 KB หน่วยความจำกายภาพที่เล็กที่สุดที่ระบบปฏิบัติการจัดสรรจะมีขนาดใหญ่กว่าในระบบ 4 KB ถึง 4 เท่า หากตัวจัดสรรที่กำหนดเองได้รับการออกแบบตามสมมติฐาน 4 KB ตัวจัดสรรจะกระจายออบเจ็กต์ขนาดเล็กไปยังหน้าขนาด 16 KB หลายหน้าและเก็บหน่วยความจำว่างไว้โดยไม่จำเป็น ซึ่งอาจ เพิ่มการใช้งานหน่วยความจำจริง (RSS) อย่างมากและลดประสิทธิภาพของ Swap ที่บีบอัด (ZRAM)
หากโค้ดของคุณจัดการพูลหน่วยความจำของตัวเอง ให้ทำตามคำแนะนำต่อไปนี้
1. หลีกเลี่ยงการกำหนดค่าขีดจำกัดไบต์แบบฮาร์ดโค้ดเพื่อเพิ่มพื้นที่ว่างในหน่วยความจำ
ตัวจัดสรรจำนวนมากใช้ขีดจำกัดไบต์คงที่เพื่อตัดสินใจว่าจะปล่อยหน่วยความจำกลับไปยังระบบปฏิบัติการโดยใช้ madvise(MADV_DONTNEED) เมื่อใด (เช่น ปล่อยหน่วยความจำก็ต่อเมื่อออบเจ็กต์ที่ใช้งานอยู่ใช้พื้นที่น้อยกว่า 8 KB)
ในระบบ 16 KB แม้แต่ออบเจ็กต์ที่ใช้งานอยู่ขนาด 16 ไบต์เพียงรายการเดียวก็จะตรึงหน้าขนาด 16 KB ทั้งหน้า (ซึ่งมากกว่า 8 KB) ด้วยเหตุนี้ จึงไม่ถึงเกณฑ์การปล่อย และ Allocator จะไม่ส่งคืนหน่วยความจำที่ไม่ได้ใช้โดยรอบไปยังเคอร์เนล
สิ่งที่ต้องทำ: อย่าใช้ค่าคงที่ไบต์แบบฮาร์ดโค้ดสำหรับฮิวริสติกการปล่อยหน้าเว็บ
ปรับเกณฑ์การเผยแพร่แบบไดนามิกที่รันไทม์ตามขนาดหน้าเว็บจริง
โดยใช้ sysconf(_SC_PAGESIZE)
2. เติมหน้าเว็บที่ใช้แล้วก่อน (การจัดสรรแบบหนาแน่นก่อน)
หากตัวจัดสรรส่งหน่วยความจำตามลำดับแบบเข้าก่อนออกก่อน (FIFO) หรือแบบหมุนเวียน การจัดสรรใหม่จะกระจายไปทั่วหน้า 16 KB ที่เติมบางส่วนจำนวนมาก ออบเจ็กต์เดียวในหน้าเว็บจะเก็บข้อมูลทั้งหมด 16 KB ไว้ใน RAM จริง
สิ่งที่ต้องทำ: จัดสรรออบเจ็กต์ใหม่จากหน้าหรือ Slab ที่เต็มที่สุด (หนาแน่นที่สุด) ก่อนเสมอ ก่อนที่จะแตะหน้าว่างหรือหน้าที่มีการใช้งานน้อย การจัดสรรใหม่โดยเน้นไปที่หน้าเว็บที่ใช้แล้วจะช่วยให้หน้าเว็บที่มีการใช้งานน้อยกลับมามีออบเจ็กต์ที่ใช้งานอยู่เป็นศูนย์ได้เองตามธรรมชาติ เพื่อให้ระบบปฏิบัติการสามารถปล่อยหน้าเว็บขนาด 16 KB ทั้งหน้าได้
3. จัดแนวพูลหน่วยความจำให้เป็น 16 KB และรักษาขนาดพูลให้อยู่ในระดับปานกลาง
ช่วงหลายหน้าที่ออกแบบมาสำหรับระบบขนาด 4 KB อาจมีหน่วยความจำที่ไม่ได้ใช้งานมากเกินไปในเคอร์เนลขนาด 16 KB นอกจากนี้ คลาสขนาดที่หารด้วย 16 KB ไม่ลงตัวจะทำให้เกิดการกระจายข้อมูลที่ส่วนท้ายของแต่ละหน้า
สิ่งที่ต้องทำ
- ตรวจสอบว่าพูลหน่วยความจำ ขอบเขตสแล็บ และการจัดแนวบัฟเฟอร์ทั้งหมดเป็น หลายเท่าของขนาดหน้าในรันไทม์
- ประเมินขนาดช่วงหลายหน้าสำหรับคลาสออบเจ็กต์ขนาดเล็กอีกครั้งเพื่อหลีกเลี่ยง การจัดสรร Slab ที่ใหญ่เกินไปซึ่งจะดักจับหน่วยความจำที่ไม่ได้ใช้งาน
4. ปล่อยหน่วยความจำจริงสำหรับบัฟเฟอร์ขนาดใหญ่ที่แคชไว้ทันที
ตัวจัดสรรที่กำหนดเองมักจะแคชบัฟเฟอร์ขนาดใหญ่ (> 64 KB) ในพูลในหน่วยความจำเพื่อให้สามารถนำกลับมาใช้ใหม่ได้โดยไม่ต้องเสียค่าใช้จ่ายเพิ่มเติมสำหรับการเรียกใช้ระบบ mmap หรือ munmap อย่างไรก็ตาม การเก็บรักษาบัฟเฟอร์ที่สกปรกเหล่านี้ไว้ในหน่วยความจำจะทำให้ RAM จริงเสียพื้นที่ไปหลายเมกะไบต์ขณะรอตัวจับเวลาการขับออก
สิ่งที่ต้องทำ: เก็บช่วงที่อยู่หน่วยความจำเสมือนที่สงวนไว้สำหรับการนำกลับมาใช้ใหม่อย่างรวดเร็ว แต่ให้เรียกใช้ madvise(..., MADV_DONTNEED) หรือ madvise(..., MADV_FREE) ทันทีเมื่อส่งคืนบัฟเฟอร์ไปยังแคช ระบบปฏิบัติการจะเรียกคืน RAM จริงทันที ในขณะที่แอปของคุณยังคงใช้ที่อยู่เสมือนซ้ำได้ทันทีโดยไม่ต้องจัดสรรใหม่
5. ล้างหน่วยความจำเมื่อว่างแทนที่จะล้างเมื่อมีการจัดสรรสำหรับการบีบอัด ZRAM
Android ใช้การสลับที่บีบอัด (ZRAM) เพื่อเก็บแอปที่ทำงานอยู่เบื้องหลังไว้ในหน่วยความจำ ในอุปกรณ์ที่มีขนาด 16 KB หากหน้าเว็บมีออบเจ็กต์ที่ใช้งานอยู่แม้เพียง 1 รายการ หน้าเว็บขนาด 16 KB ทั้งหมด จะยังคงอยู่ในหน่วยความจำหรือสลับไปยัง ZRAM ข้อมูลขยะที่เหลืออยู่ (เช่น พอยน์เตอร์และสตริงที่ไม่มีการใช้งาน) ในส่วนที่ว่างก่อนหน้านี้ของหน้าเว็บนั้นจะบีบอัดได้ไม่ดีนัก
หาก Allocator ของคุณล้างหน่วยความจำ (เช่น เพื่อความปลอดภัยหรือการจัดสรรที่เริ่มต้นเป็น 0) ให้พิจารณาล้างเมื่อยกเลิกการจัดสรร (free()) แทนที่จะล้างเมื่อจัดสรร
- หน้าเว็บที่ปักหมุดมีค่าใช้จ่ายน้อยกว่า: หน่วยความจำที่ไม่ได้ใช้งานในหน้าขนาด 16 KB ที่เติมบางส่วน จะบีบอัดลงจนแทบไม่มีอะไรเหลือใน ZRAM ดังนั้นหน้าเว็บที่ปักหมุดจึงไม่ ทำให้พื้นที่ Swap จริงสูญเปล่า
- ค่าใช้จ่ายของแคชน้อยที่สุด: เมื่อมีการเรียกใช้
free()หน่วยความจำจะ อยู่ในแคชของ CPU อยู่แล้ว ซึ่งจะช่วยหลีกเลี่ยงการไม่พบแคชเพิ่มเติมในภายหลัง
สรุปคำแนะนำ
| พื้นที่ | คำแนะนำ | ผลกระทบที่คาดหวัง |
|---|---|---|
| เกณฑ์การล้างข้อมูล | ปรับขนาดเกณฑ์การเผยแพร่แบบไดนามิกโดยใช้ sysconf(_SC_PAGESIZE) |
ป้องกันไม่ให้ตรรกะการเผยแพร่เกิดภาวะติดตายอย่างถาวร |
| ลำดับการจัดสรร | จัดสรรจากหน้าหรือสแล็บที่มีความหนาแน่นมากที่สุด (เกือบเต็ม) ก่อน | ลดหน่วยความจำที่ใช้งาน (RSS) โดยการรวมออบเจ็กต์ที่ใช้งานอยู่ไว้ด้วยกัน |
| การกำหนดขนาดช่วง | จัดคลาสขนาดให้สอดคล้องกับจำนวนทวีคูณของ 16 KB และหลีกเลี่ยง Slab ที่มีขนาดใหญ่เกินไป | กำจัดการกระจาย Fragment ของหน้าส่วนท้ายและลดหน่วยความจำที่ไม่ได้ใช้ |
| แคชบัฟเฟอร์ | เรียกใช้ madvise(MADV_DONTNEED) ทันทีเมื่อแคชบัฟเฟอร์ขนาดใหญ่ |
กำจัด RAM ที่ไม่ได้ใช้งานซึ่งทำให้เครื่องช้าลง ขณะเดียวกันก็ยังคงใช้ RAM เสมือนได้อย่างรวดเร็ว |
| Swap / ZRAM | ล้างหน่วยความจำใน free() แทนที่จะล้างในการจัดสรร |
ปรับปรุงอัตราส่วนการบีบอัด ZRAM เพื่อให้หน้าเว็บที่ปักหมุดใช้ทรัพยากรน้อยลง |
ตรวจสอบ SDK เพื่อดูการรองรับขนาด 16 KB
SDK จำนวนมากเข้ากันได้กับขนาดหน้า 16 KB โดยเฉพาะอย่างยิ่งหากคุณสร้างขึ้นเองหรือใช้เวอร์ชันที่สร้างไว้ล่วงหน้าล่าสุด อย่างไรก็ตาม เนื่องจาก SDK ที่สร้างไว้ล่วงหน้าหรือ SDK บางเวอร์ชันไม่รองรับขนาด 16 KB คุณจึงควรตรวจสอบเว็บไซต์ของผู้ให้บริการ SDK แต่ละรายเพื่อดูว่าควรใช้เวอร์ชันใดกับขนาด 16 KB
ทดสอบแอปในสภาพแวดล้อมแบบ 16 KB
หลังจากสร้างแอปที่รองรับอุปกรณ์แบบ 16 KB แล้ว คุณจะต้อง ทดสอบแอปในสภาพแวดล้อมแบบ 16 KB เพื่อดูว่าแอปของคุณมี การถดถอยหรือไม่ โดยทำตามขั้นตอนต่อไปนี้
ตั้งค่า Android 15 SDK ขึ้นไป
ตั้งค่าสภาพแวดล้อมการทดสอบอย่างใดอย่างหนึ่งต่อไปนี้
เปิดอุปกรณ์ทดสอบ จากนั้นเรียกใช้คำสั่งต่อไปนี้เพื่อยืนยันว่า อุปกรณ์ใช้สภาพแวดล้อมแบบ 16 KB
adb shell getconf PAGE_SIZEคำสั่งควรแสดงค่า
16384เรียกใช้คำสั่ง
zipalignต่อไปนี้เพื่อยืนยันว่าแอปจัดแนวแบบ 16 KB โดยที่ APK_NAME คือชื่อไฟล์ APK ของแอปzipalign -c -P 16 -v 4 APK_NAME.apkทดสอบแอปอย่างละเอียด โดยมุ่งเน้นที่ส่วนใดก็ตามที่อาจได้รับผลกระทบจาก การเปลี่ยนอินสแตนซ์โค้ดที่อ้างอิงขนาดหน้าเว็บที่เฉพาะเจาะจง
ตั้งค่าโปรแกรมจำลองของ Android ด้วยอิมเมจระบบแบบ 16 KB
หากต้องการตั้งค่าสภาพแวดล้อม 16 KB โดยใช้โปรแกรมจำลองของ Android ให้ทำตามขั้นตอนต่อไปนี้
- ใน Android Studio ให้คลิกเครื่องมือ > เครื่องมือจัดการ SDK
ในแท็บแพลตฟอร์ม SDK ให้เลือกแสดงรายละเอียดแพ็กเกจ แล้วขยายส่วน Android VanillaIceCream ขึ้นไป แล้วเลือกอิมเมจระบบโปรแกรมจำลองต่อไปนี้อย่างใดอย่างหนึ่งหรือทั้ง 2 อย่าง โดยขึ้นอยู่กับอุปกรณ์เสมือนจริงที่คุณต้องการสร้าง
- อิมเมจระบบ ARM 64 v8a ขนาดหน้า 16 KB เวอร์ชันทดลองของ Google APIs
- อิมเมจระบบ Intel x86_64 Atom ขนาดหน้า 16 KB เวอร์ชันทดลองของ Google APIs
คลิกใช้ > ตกลงเพื่อดาวน์โหลดอิมเมจระบบที่คุณเลือก
ทำตามขั้นตอนเพื่อตั้งค่าอุปกรณ์เสมือนจริงสำหรับ Android 15 และเมื่อระบบแจ้งให้เลือกอิมเมจระบบ ให้เลือกอิมเมจระบบขนาด 16 KB ที่คุณดาวน์โหลดไว้ หากระบบไม่แนะนำโดยอัตโนมัติ คุณจะดูอิมเมจระบบขนาด 16 KB ได้ในแท็บอิมเมจอื่นๆ
เปิดโปรแกรมจำลอง
หลังจากตั้งค่าโปรแกรมจำลองของ Android และอุปกรณ์เสมือนจริงเสร็จแล้ว ให้เปิดโปรแกรมจำลองจากเมนูอุปกรณ์เป้าหมายหรือจากบรรทัดคำสั่ง
เปิดใช้โหมด 16 KB ในอุปกรณ์โดยใช้ตัวเลือกสำหรับนักพัฒนาแอป
สลับตัวเลือกสำหรับนักพัฒนาแอปบูตโดยใช้หน้าหน่วยความจำขนาด 16 KB เพื่อบูตอุปกรณ์ในโหมด 16 KB
ใน Android 15 เวอร์ชัน QPR คุณสามารถใช้ตัวเลือกสำหรับนักพัฒนาแอปที่มีในอุปกรณ์บางรุ่นเพื่อบูตอุปกรณ์ในโหมด 16 KB และทำการทดสอบในอุปกรณ์ได้ ก่อนใช้ตัวเลือกสำหรับนักพัฒนาซอฟต์แวร์ ให้ไปที่การตั้งค่า > ระบบ > การอัปเดตซอฟต์แวร์ แล้วใช้การอัปเดตที่มี
ตัวเลือกสำหรับนักพัฒนาแอปนี้พร้อมใช้งานในอุปกรณ์ต่อไปนี้
Pixel 8 และ 8 Pro (ที่ใช้ Android 15 QPR1 ขึ้นไป)
Pixel 8a (ใช้ Android 15 QPR1 ขึ้นไป)
Pixel 9, 9 Pro และ 9 Pro XL (ใช้ Android 15 QPR2 ขึ้นไป)
Pixel 9a (ใช้ Android 16 ขึ้นไป)
โหมดความเข้ากันได้แบบย้อนหลัง 16 KB
คำเตือนในโหมดความเข้ากันได้กับขนาดหน้า
ตัวเลือกความเข้ากันได้แบบย้อนหลัง 16 KB จะพร้อมใช้งานเมื่ออุปกรณ์ใช้ เคอร์เนลขนาด 16 KB Package Manager จะเรียกใช้แอปในโหมดความเข้ากันได้ย้อนหลัง 16 KB เมื่อตรงตามเงื่อนไขต่อไปนี้
- หากแอปมีไฟล์ ELF (ที่มีนามสกุล
.so) ที่มีการจัดแนวกลุ่ม LOAD ขนาด 4 KB - หาก APK ที่บีบอัดมีไฟล์ ELF ที่ไม่ได้บีบอัดซึ่งจัดเรียง ZIP ขนาด 4 KB
หากเครื่องมือจัดการแพ็กเกจเปิดใช้โหมดความเข้ากันได้แบบย้อนหลัง 16 KB สำหรับแอป แอปจะแสดงคำเตือนเมื่อเปิดใช้งานครั้งแรกว่ากำลังทำงานในโหมดความเข้ากันได้แบบย้อนหลัง 16 KB
โหมดความเข้ากันได้แบบย้อนหลัง 16 KB ช่วยให้แอปบางแอปทำงานได้ แต่เพื่อให้มีความน่าเชื่อถือและความเสถียรสูงสุด แอปควรได้รับการจัดแนวให้สอดคล้องกับขนาดหน้า 16 KB
ในหน้าข้อมูลแอป ให้สลับการตั้งค่าเรียกใช้แอปด้วย โหมดเข้ากันได้กับขนาดหน้าในส่วนขั้นสูงเพื่อเปิดหรือปิดใช้โหมดความเข้ากันได้แบบย้อนกลับ 16 KB สำหรับแอปที่เฉพาะเจาะจง การตั้งค่านี้จะปรากฏขึ้นเมื่ออุปกรณ์ทำงานด้วย หน้าหน่วยความจำขนาด 16 KB เท่านั้น
การตั้งค่าโหมดเข้ากันได้กับขนาดหน้า
วิธีบังคับให้ใช้การรองรับการทำงานร่วมกันแบบย้อนหลังขนาด 16 KB สำหรับทุกแอปในอุปกรณ์
adb shell setprop bionic.linker.16kb.app_compat.enabled true
adb shell setprop pm.16kb.app_compat.disabled false
วิธีบังคับปิดความเข้ากันได้แบบย้อนหลัง 16 KB สำหรับทุกแอปในอุปกรณ์
adb shell setprop bionic.linker.16kb.app_compat.enabled false
adb shell setprop pm.16kb.app_compat.disabled true
ใน Android 17 คุณยังสามารถบังคับปิดความเข้ากันได้แบบย้อนหลัง 16 KB สำหรับทุกแอปและทำให้ไบนารีที่เข้ากันไม่ได้หยุดทำงานทันทีได้ด้วย โดยทำดังนี้
adb shell setprop bionic.linker.16kb.app_compat.enabled fatal
adb shell setprop pm.16kb.app_compat.disabled true
ตั้งค่าพร็อพเพอร์ตี้ android:pageSizeCompat เป็นเปิดใช้หรือปิดใช้เพื่อเปิดหรือปิดโหมดความเข้ากันได้แบบย้อนหลังสำหรับแอปที่เฉพาะเจาะจงใน AndroidManifest.xml เมื่อตั้งค่าพร็อพเพอร์ตี้นี้ แอปจะไม่แสดงคำเตือนโหมดความเข้ากันได้แบบย้อนหลังเมื่อเปิดแอป