المشاكل الشائعة وحلولها

هذا المستند هو قائمة جزئية بالأخطاء الأكثر شيوعًا التي قد تواجهها عند استخدام NDK، بالإضافة إلى حلولها (إذا كانت متوفرة).

استخدام _FILE_OFFSET_BITS=64 مع مستويات واجهة برمجة التطبيقات القديمة

قبل العناوين الموحّدة، لم يكن NDK يتيح استخدام _FILE_OFFSET_BITS=64. وإذا كنت قد حدّدته عند إنشاء تطبيقك، سيتم تجاهله بدون إشعارك. يتوافق الخيار _FILE_OFFSET_BITS=64 الآن مع العناوين الموحّدة، ولكن في الإصدارات القديمة من Android، لم يتوفّر سوى عدد قليل جدًا من واجهات برمجة التطبيقات off_t كإصدار off64_t. لذلك، يؤدي استخدام هذه الميزة مع مستويات واجهة برمجة التطبيقات القديمة إلى توفّر عدد أقل من الوظائف.

تم شرح هذه المشكلة بالتفصيل في منشور المدونة r16 وفي مستندات bionic.

المشكلة: يطلب الإصدار واجهات برمجة تطبيقات غير متوفّرة في 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 أعلى من مستوى واجهة برمجة التطبيقات للجهاز

يختلف مستوى واجهة برمجة التطبيقات الذي تستخدمه مع NDK عن مستوى واجهة برمجة التطبيقات compileSdkVersion الذي تستخدمه مع Java. مستوى واجهة برمجة التطبيقات في NDK هو الحد الأدنى لمستوى واجهة برمجة التطبيقات المتوافق مع تطبيقك. في ndk-build، هذا هو إعداد APP_PLATFORM. باستخدام CMake، يكون هذا -DANDROID_PLATFORM.

بما أنّه يتم عادةً تحديد مراجع الدوال عند تحميل المكتبات وليس عند استدعائها للمرة الأولى، لا يمكنك الرجوع إلى واجهات برمجة التطبيقات التي لا تكون متاحة دائمًا وحماية استخدامها من خلال عمليات التحقّق من مستوى واجهة برمجة التطبيقات. وإذا تمت الإشارة إليها، يجب أن تكون متوفّرة.

المشكلة: مستوى واجهة برمجة التطبيقات في NDK أعلى من مستوى واجهة برمجة التطبيقات المتوافق مع جهازك.

الحل: اضبط مستوى واجهة برمجة التطبيقات في 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 التفصيلية هذه.

مرجع غير محدّد إلى __atomic_*

المشكلة: تحتاج بعض واجهات ABI إلى libatomic لتوفير بعض عمليات التنفيذ للعمليات الذرية.

الحلّ: أضِف -latomic عند الربط.

بالنسبة إلى رسالة الخطأ التالية:

error: undefined reference to '__atomic_exchange_4'

قد يكون الرمز الفعلي هنا أي شيء مسبوقًا بـ __atomic_.

عدم عمل RTTI/الاستثناءات على مستوى حدود المكتبة

المشكلة: لا يتم رصد الاستثناءات عند طرحها على مستوى حدود المكتبة المشتركة، أو يتعذّر تنفيذ dynamic_cast.

الحلّ: أضِف دالة مفتاح إلى أنواعك. الدالة الرئيسية هي أول دالة افتراضية غير خالصة وخارجية لنوع معيّن. للاطّلاع على مثال، راجِع المناقشة حول المشكلة 533.

توضّح واجهة التطبيق الثنائية (ABI) للغة C++‎ أنّ عنصرَين لهما النوع نفسه إذا كانت مؤشرات type_info متطابقة فقط. لا يمكن رصد الاستثناءات إلا إذا كان type_info الخاص بالرصد يطابق الاستثناء الذي تم طرحه. وينطبق الأمر نفسه على dynamic_cast.

عندما لا يكون للنوع دالة مفتاحية، يتم إصدار typeinfo كرمز ضعيف ويتم دمج معلومات النوع المطابقة عند تحميل المكتبات. عند تحميل المكتبات ديناميكيًا بعد تحميل الملف التنفيذي (أي من خلال dlopen أو System.loadLibrary)، قد لا يتمكّن برنامج التحميل من دمج معلومات الأنواع الخاصة بالمكتبات المحمَّلة. في هذه الحالة، لا يُعتبر النوعان متساويين.

استخدام مكتبات مجمَّعة مسبقًا غير متطابقة

يتطلّب استخدام المكتبات المُنشأة مسبقًا، والتي تكون عادةً مكتبات تابعة لجهات خارجية، بعض العناية الإضافية في تطبيقك. بشكل عام، يجب الانتباه إلى القواعد التالية:

  • إنّ الحد الأدنى لمستوى واجهة برمجة التطبيقات للتطبيق الناتج هو الحد الأقصى لقيم minSdkVersion لجميع مكتبات التطبيق.

    إذا كان minSdkVersion هو 16، ولكنك تستخدم مكتبة مسبقة الإنشاء تم إنشاؤها باستخدام المستوى 21، سيكون الحد الأدنى لمستوى واجهة برمجة التطبيقات للتطبيق الناتج هو 21. سيظهر عدم الالتزام بذلك في مدّة التصميم إذا كانت المكتبة المسبقة الإنشاء ثابتة، ولكن قد لا يظهر إلا في وقت التشغيل للمكتبات المشتركة المسبقة الإنشاء.

  • يجب إنشاء جميع المكتبات باستخدام إصدار NDK نفسه.

    هذه القاعدة أكثر مرونة من معظم القواعد الأخرى لأنّ حالات عدم التوافق نادرة، ولكن لا يمكن ضمان التوافق بين المكتبات التي تم إنشاؤها باستخدام إصدارات رئيسية مختلفة من NDK. إنّ واجهة التطبيق الثنائية (ABI) للغة C++ غير ثابتة وقد تغيّرت في الماضي.

  • يجب أن تستخدم التطبيقات التي تتضمّن عدة مكتبات مشتركة مكتبة STL مشتركة.

    وكما هو الحال مع ملفات الترجمة والشرح غير المتطابقة، يمكن تجنُّب المشاكل الناتجة عن ذلك إذا تم توخّي الحذر الشديد، ولكن من الأفضل تجنُّب المشكلة تمامًا. أفضل طريقة لتجنُّب هذه المشكلة هي عدم توفُّر مكتبات مشتركة متعددة في تطبيقك.