ปัญหาที่พบบ่อยและวิธีแก้ไข

เอกสารนี้เป็นรายการบางส่วนของปัญหาที่ไม่ใช่ข้อบกพร่องที่พบบ่อยที่สุดซึ่งคุณอาจพบเมื่อใช้ NDK พร้อมวิธีแก้ปัญหา (หากมี)

การใช้ _FILE_OFFSET_BITS=64 กับ API ระดับเก่า

ก่อนที่จะมีส่วนหัวแบบรวม NDK ไม่รองรับ _FILE_OFFSET_BITS=64 หากคุณกำหนดค่านี้ไว้เมื่อสร้างแอป ระบบจะละเว้นค่าดังกล่าวโดยไม่มีการแจ้งเตือน ตอนนี้ตัวเลือก _FILE_OFFSET_BITS=64 ได้รับการรองรับในส่วนหัวแบบรวมแล้ว แต่ใน Android เวอร์ชันเก่า มี API off_t เพียงไม่กี่รายการเท่านั้นที่พร้อมใช้งานเป็นตัวแปร off64_t ดังนั้น การใช้ฟีเจอร์นี้กับ API ระดับเก่าจะทำให้มีฟังก์ชันพร้อมใช้งานน้อยลง

เราได้อธิบายปัญหานี้โดยละเอียดในบล็อกโพสต์ r16 และในเอกสารประกอบ ของ Bionic

ปัญหา: บิลด์ขอใช้ API ที่ไม่มีอยู่ใน minSdkVersion

วิธีแก้ปัญหา: ปิดใช้ _FILE_OFFSET_BITS=64 หรือเพิ่ม minSdkVersion

mmap ที่ไม่ได้ประกาศหรือกำหนดไว้โดยนัย

คุณอาจเห็นข้อผิดพลาดต่อไปนี้ใน C++

error: use of undeclared identifier 'mmap'

หรือข้อผิดพลาดต่อไปนี้ใน C

warning: implicit declaration of function 'mmap' is invalid in C99

การใช้ _FILE_OFFSET_BITS=64 จะสั่งให้ไลบรารี C ใช้ mmap64 แทน mmap mmap64 ไม่พร้อมใช้งานจนกว่าจะถึง android-21 หากค่า minSdkVersion ต่ำกว่า 21 ไลบรารี C จะไม่มี mmap ที่เข้ากันได้กับ _FILE_OFFSET_BITS=64 ฟังก์ชันจึงไม่พร้อมใช้งาน

minSdkVersion ตั้งค่าสูงกว่าระดับ API ของอุปกรณ์

API ระดับที่คุณสร้างด้วย NDK มีความหมายแตกต่างจาก compileSdkVersion สำหรับ Java มาก ระดับ API ของ NDK คือ ระดับ API ต่ำสุด ที่แอปของคุณรองรับ ใน ndk-build การตั้งค่านี้คือ APP_PLATFORM ส่วนใน CMake คือ -DANDROID_PLATFORM

เนื่องจากโดยปกติแล้วการอ้างอิงฟังก์ชันจะได้รับการแก้ไขเมื่อโหลดไลบรารี ไม่ใช่เมื่อมีการเรียกใช้ฟังก์ชันเป็นครั้งแรก คุณจึงอ้างอิง API ที่ไม่ได้มีอยู่เสมอไปและป้องกันการใช้งานด้วยการตรวจสอบระดับ API ไม่ได้ หากมีการอ้างอิง API เหล่านั้น API จะต้องมีอยู่

ปัญหา: ระดับ API ของ NDK สูงกว่า API ที่อุปกรณ์รองรับ

วิธีแก้ปัญหา: ตั้งค่าระดับ API ของ NDK (APP_PLATFORM) เป็น Android เวอร์ชันต่ำสุด ที่แอปของคุณรองรับ

ระบบบิลด์ การตั้งค่า
ndk-build APP_PLATFORM
CMake ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

สำหรับระบบบิลด์อื่นๆ โปรดดู ใช้ NDK กับระบบบิลด์ อื่นๆ

หาไม่พบสัญลักษณ์ __aeabi

ข้อความต่อไปนี้

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aeabi_memcpy"

เป็นตัวอย่างหนึ่งของข้อผิดพลาดที่อาจเกิดขึ้น ขณะรันไทม์ ข้อผิดพลาดเหล่านี้จะปรากฏในบันทึกเมื่อคุณพยายามโหลดไลบรารีเนทีฟ สัญลักษณ์อาจเป็น __aeabi_* ใดก็ได้ โดย __aeabi_memcpy และ __aeabi_memclr ดูเหมือนจะเป็นสัญลักษณ์ที่พบบ่อยที่สุด

เราได้บันทึกปัญหานี้ไว้ใน ปัญหาที่ 126

หาไม่พบสัญลักษณ์ rand

สำหรับข้อความในบันทึกข้อผิดพลาดต่อไปนี้

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "rand"

โปรดดูคำตอบโดยละเอียดนี้ใน Stack Overflow answer

การอ้างอิง __atomic_* ที่ไม่ได้กำหนดไว้

ปัญหา: ABI บางรายการต้องใช้ libatomic เพื่อให้การใช้งานบางอย่างสำหรับการดำเนินการแบบ อะตอมมิก

วิธีแก้ปัญหา: เพิ่ม -latomic เมื่อลิงก์

สำหรับข้อความแสดงข้อผิดพลาดต่อไปนี้

error: undefined reference to '__atomic_exchange_4'

สัญลักษณ์จริงในที่นี้อาจเป็นสัญลักษณ์ใดก็ได้ที่ขึ้นต้นด้วย __atomic_

RTTI/ข้อยกเว้นไม่ทำงานข้ามขอบเขตของไลบรารี

ปัญหา: ระบบไม่ดักจับข้อยกเว้นเมื่อมีการส่งข้อยกเว้นข้ามขอบเขตของไลบรารีที่ใช้ร่วมกัน หรือ dynamic_cast ล้มเหลว

วิธีแก้ปัญหา: เพิ่มฟังก์ชันหลักลงในประเภท ฟังก์ชันหลักคือฟังก์ชันเสมือนแบบอินไลน์ที่ไม่ใช่ฟังก์ชันเสมือนแบบเพียวฟังก์ชันแรกสำหรับประเภท ดูตัวอย่างได้ที่ การสนทนาใน ปัญหาที่ 533

ABI ของ C++ ระบุว่าออบเจ็กต์ 2 รายการมีประเภทเดียวกันก็ต่อเมื่อ ตัวชี้ type_info ของออบเจ็กต์เหมือนกัน ระบบจะดักจับข้อยกเว้นได้ก็ต่อเมื่อ type_info สำหรับการดักจับตรงกับข้อยกเว้นที่ส่ง dynamic_cast ก็ใช้กฎเดียวกัน

เมื่อประเภทไม่มีฟังก์ชันหลัก ระบบจะส่ง typeinfo เป็นสัญลักษณ์แบบอ่อน และระบบจะผสานข้อมูลประเภทที่ตรงกันเมื่อโหลดไลบรารี เมื่อโหลดไลบรารีแบบไดนามิกหลังจากโหลดไฟล์ปฏิบัติการแล้ว (กล่าวคือ ผ่าน dlopen หรือ System.loadLibrary) ตัวโหลดอาจผสานข้อมูลประเภทสำหรับไลบรารีที่โหลดไม่ได้ เมื่อเกิดกรณีนี้ ระบบจะไม่ถือว่าประเภททั้ง 2 เท่ากัน

การใช้ไลบรารีที่สร้างไว้ล่วงหน้าที่ไม่ตรงกัน

การใช้ไลบรารีที่สร้างไว้ล่วงหน้า ซึ่งโดยปกติจะเป็นไลบรารีของบุคคลที่สามในแอปพลิเคชันของคุณต้องใช้ความระมัดระวังเป็นพิเศษ โดยทั่วไป ให้ระวังกฎต่อไปนี้

  • ระดับ API ต่ำสุดของแอปที่ได้คือค่าสูงสุดของ minSdkVersion ของไลบรารีทั้งหมดของแอป

    หาก minSdkVersion ของคุณคือ 16 แต่คุณใช้ไลบรารีที่สร้างไว้ล่วงหน้าที่สร้างขึ้นสำหรับ 21 ระดับ API ต่ำสุดของแอปที่ได้คือ 21 หากไม่ปฏิบัติตามกฎนี้ คุณจะเห็นข้อผิดพลาดในเวลาบิลด์หากไลบรารีที่สร้างไว้ล่วงหน้าเป็นแบบคงที่ แต่ข้อผิดพลาดอาจไม่ปรากฏจนกว่าจะถึงเวลาเรียกใช้สำหรับไลบรารีที่ใช้ร่วมกันที่สร้างไว้ล่วงหน้า

  • ควรสร้างไลบรารีทั้งหมดด้วย NDK เวอร์ชันเดียวกัน

    กฎนี้มีความยืดหยุ่นมากกว่ากฎอื่นๆ เนื่องจากข้อผิดพลาดเกิดขึ้นไม่บ่อยนัก แต่เราไม่รับประกันความเข้ากันได้ระหว่างไลบรารีที่สร้างขึ้นด้วย NDK เวอร์ชันหลักที่แตกต่างกัน ABI ของ C++ ไม่เสถียรและมีการเปลี่ยนแปลงในอดีต

  • แอปที่มีไลบรารีที่ใช้ร่วมกันหลายรายการต้องใช้ shared STL

    เช่นเดียวกับ STL ที่ไม่ตรงกัน คุณสามารถหลีกเลี่ยงปัญหาที่เกิดจากกรณีนี้ได้หากใช้ความระมัดระวังอย่างมาก แต่การหลีกเลี่ยงปัญหาตั้งแต่แรกย่อมดีกว่า วิธีที่ดีที่สุดในการหลีกเลี่ยงปัญหานี้คือการหลีกเลี่ยงการมีไลบรารีที่ใช้ร่วมกันหลายรายการในแอป