במסמך הזה מופיעה רשימה חלקית של בעיות נפוצות שאתם עשויים להיתקל בהן במהלך השימוש ב-NDK, ופתרונות לבעיות האלה (אם יש כאלה).
שימוש ב-_FILE_OFFSET_BITS=64
עם רמות API ישנות יותר
לפני הכותרות המאוחדות, ה-NDK לא תמך ב-_FILE_OFFSET_BITS=64
. אם הגדרתם אותו כשבניתם את האפליקציה, הוא התעלם ממנו בשקט. האפשרות _FILE_OFFSET_BITS=64
נתמכת עכשיו עם כותרות מאוחדות, אבל בגרסאות ישנות של Android, רק מעט ממשקי ה-API של off_t
היו זמינים כגרסת off64_t
. לכן, שימוש בתכונה הזו עם רמות API ישנות יותר יגרום לכך שיהיו פחות פונקציות זמינות.
הבעיה הזו מוסברת בפירוט בפוסט בבלוג בנושא r16 ובמסמכי bionic.
בעיה: ה-build מבקש ממשקי API שלא קיימים ב-minSdkVersion
.
הפתרון: משביתים את _FILE_OFFSET_BITS=64
או מרימים את minSdkVersion
.
הגדרה לא מוצהרת או מרומזת של mmap
יכול להיות שתופיע השגיאה הבאה ב-C++:
שגיאה: נעשה שימוש במזהה שלא הוגדר '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
set higher than device API level
לרמת ה-API שאתם יוצרים באמצעות NDK יש משמעות שונה מאוד מזו של compileSdkVersion
ב-Java. רמת ה-API של NDK היא רמת ה-API המינימלית שנתמכת באפליקציה. ב-ndk-build, זו ההגדרה APP_PLATFORM
. ב-CMake, זה -DANDROID_PLATFORM
.
מכיוון שהפניות לפונקציות בדרך כלל נפתרות כשהספריות נטענות ולא כשהן נקראות בפעם הראשונה, אי אפשר להפנות לממשקי API שלא תמיד קיימים ולשמור על השימוש בהם באמצעות בדיקות ברמת ה-API. אם יש הפניה אליהם, הם חייבים להיות נוכחים.
בעיה: רמת ה-API של NDK גבוהה מרמת ה-API שנתמכת במכשיר.
פתרון: צריך להגדיר את רמת ה-API של NDK (APP_PLATFORM
) לגרסה המינימלית של Android שהאפליקציה תומכת בה.
מערכת build | הגדרה |
---|---|
ndk-build | APP_PLATFORM |
CMake | ANDROID_PLATFORM |
externalNativeBuild | android.minSdkVersion |
למידע על מערכות build אחרות, אפשר לעיין במאמר שימוש ב-NDK עם מערכות build אחרות.
לא ניתן למצוא את הסמלים של __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
כשמקשרים.
לגבי הודעת השגיאה הבאה:
שגיאה: הפניה לא מוגדרת אל '
__atomic_exchange_4
'
הסמל בפועל יכול להיות כל דבר שמתחיל ב-__atomic_
.
החריגים או ה-RTTI לא פועלים בין גבולות הספרייה
בעיה: חריגים לא נתפסים כשהם מופעלים מעבר לגבולות של ספרייה משותפת, או שהפונקציה dynamic_cast
נכשלת.
הפתרון: מוסיפים פונקציית מפתח לסוגים. פונקציה מרכזית היא הפונקציה הווירטואלית הראשונה שאינה טהורה, שאינה מוטמעת בשורה, עבור סוג מסוים. לדוגמה, אפשר לעיין בדיון בנושא Issue 533.
ב-C++ ABI מצוין ששני אובייקטים הם מאותו סוג אם ורק אם מצביעי type_info
שלהם זהים. אפשר ללכוד חריגים רק אם type_info
של ה-catch תואם לחריג שהושלך. אותו כלל חל על dynamic_cast
.
אם לסוג אין פונקציית מפתח, ה-typeinfo
שלו מופק כסמל חלש, ופרטי הסוג התואמים ממוזגים כשהספריות נטענות. כשמטעינים ספריות באופן דינמי אחרי שהקובץ להפעלה נטען (במילים אחרות, באמצעות dlopen
או System.loadLibrary
), יכול להיות שלא תהיה אפשרות למטען למזג את פרטי הסוג של הספריות שנטענו. במקרה כזה, שני הסוגים לא נחשבים שווים.
שימוש בספריות מוכנות מראש שלא תואמות
שימוש בספריות מוכנות מראש – בדרך כלל ספריות של צד שלישי – באפליקציה דורש קצת יותר זהירות. באופן כללי, חשוב להכיר את הכללים הבאים:
רמת ה-API המינימלית של האפליקציה שמתקבלת היא המקסימום של
minSdkVersion
של כל הספריות של האפליקציה.אם הערך של
minSdkVersion
הוא 16, אבל אתם משתמשים בספרייה מוכנה מראש שנבנתה על בסיס 21, רמת ה-API המינימלית של האפליקציה שמתקבלת היא 21. אם לא תפעלו בהתאם להנחיות האלה, הבעיה תהיה גלויה בזמן הבנייה אם הספרייה המוכנה מראש היא סטטית, אבל יכול להיות שהיא לא תופיע עד זמן הריצה בספריות משותפות מוכנות מראש.כל הספריות צריכות להיווצר עם אותה גרסת NDK.
הכלל הזה קצת יותר גמיש מרוב הכללים, כי נדיר שמתרחשות בעיות, אבל אין ערובה לתאימות בין ספריות שנבנו עם גרסאות ראשיות שונות של NDK. ממשק ה-ABI של C++ לא יציב והשתנה בעבר.
באפליקציות עם כמה ספריות משותפות צריך להשתמש ב-STL משותפת.
כמו במקרה של קובצי STL לא תואמים, אפשר להימנע מהבעיות שנגרמות מכך אם מקפידים על זהירות, אבל עדיף פשוט להימנע מהבעיה. הדרך הטובה ביותר להימנע מהבעיה הזו היא להימנע משימוש בכמה ספריות משותפות באפליקציה.