Распространенные проблемы и решения

В этом документе представлен неполный список наиболее часто встречающихся проблем, не являющихся ошибками, которые могут возникнуть при использовании 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++ может появиться следующая ошибка:

ошибка: использование необъявленного идентификатора 'mmap'

или следующая ошибка в языке C:

Предупреждение: неявное объявление функции 'mmap' недопустимо в 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 вашего 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 .

Неопределенная ссылка на __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, проблем, вызванных этим, можно избежать, если проявлять большую осторожность, но лучше просто избегать этой проблемы. Лучший способ избежать этой проблемы — не использовать несколько общих библиотек в вашем приложении.