Android L Preview не выполняет поиск собственных библиотек в папке armeabi (UnsatisfiedLinkError)

У меня есть приложение с двумя собственными библиотеками. 1-й работает намного быстрее на ARMv7, поэтому у меня есть версия как для ARMv7, так и для ARMv5. Второй работает одинаково на обеих платформах, поэтому предоставляется только библиотека ARMv5.

Папка моей родной библиотеки выглядит так:

/jniLibs/
    |
    +---armeabi/
    |     |
    |     +---libFirstLibrary.so
    |     +---libSecondLibrary.so
    |
    +---armeabi-v7a/
          |
          +---libFirstLibrary.so

Приложение хорошо работает на всех устройствах и версиях Android в производстве.

Когда я тестировал его на своем Nexus 5 с L-Preview (hammerhead-lpv79-preview-ac1d8a8e.tgz), я получаю эту ошибку:

java.lang.UnsatisfiedLinkError: Couldn't load SecondLibrary from loader dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.package-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.package-1, /vendor/lib, /system/lib]]]: findLibrary returned null
   at java.lang.Runtime.loadLibrary(Runtime.java:358)
   at java.lang.System.loadLibrary(System.java:610)

Проблема в том, что, несмотря на то, что в Nexus 5 CPU_ABI установлено на armeabi-v7a, а CPU_ABI2 установлено на armeabi, L-Preview использует только значение CPU_ABI и ищет «SecondLibrary» только в папке «armeabi-v7a» и вылетает, поскольку его там нет.

Когда я копирую файл .so также в папку «armeabi-v7a», все в порядке, но APK на 3,5 МБ больше, что мне не очень нравится.

Это просто ошибка Android L-Preview или какая-то «новая функция»?


person xsveda    schedule 03.09.2014    source источник


Ответы (1)


Насколько я знаю, это всегда было предполагаемым поведением, и я озадачен, почему это сработало для вас раньше.

Компоновщик не просматривает полный APK-файл при каждом вызове loadLibrary — вместо этого правильные собственные библиотеки извлекаются при установке APK. Используется только один каталог архитектуры, поэтому, если он найдет lib/armeabi-v7a, он даже не будет искать lib/armeabi.

В некоторых более старых версиях Android (4.0.3 и более ранних) существует известная проблема, когда armeabi может быть случайно использован вместо armeabi-v7a, но не уверен, что именно поэтому это работает для вас.

См., например. https://android.googlesource.com/platform/ndk/+/532389e89c/docs/text/CPU-ARCH-ABIS.text для официального объяснения этого (раздел «III. Управление ABI на платформе Android», в частности подраздел III.3 и примечание в конце подраздела III.1).

РЕДАКТИРОВАТЬ: На самом деле кажется, что менеджер пакетов может установить некоторые файлы из вторичного каталога ABI, даже если существует первичный каталог ABI, в версиях Android до kitkat. http://albin.abo.fi/~mstorsjo/hellojni-test-abis.zip является источником моего тестового примера, а http://albin.abo.fi/~mstorsjo/hellojni-test-abis.apk — это его двоичный файл. В этом примере создаются четыре собственные библиотеки: libgello-jni.so, libhello-jni.so, libhello-jni2.so и libtello-jni.so. Эти файлы создаются для всех ABI, но в armeabi-v7a я удалил все файлы, кроме libhello-jni.so — список файлов (для каталогов архитектуры ARM) выглядит следующим образом:

lib/armeabi/libgello-jni.so
lib/armeabi/libhello-jni.so
lib/armeabi/libhello-jni2.so
lib/armeabi/libtello-jni.so
lib/armeabi-v7a/libhello-jni.so

При установке на устройство kitkat armeabi-v7a это устанавливает libhello-jni.so из каталога armeabi-v7a, а libhello-jni2.so и libgello-jni.so из каталога armeabi, но libtello-jni.so вообще не устанавливается. Какие файлы устанавливаются, по-видимому, зависит от имен и порядка в APK. Таким образом, в зависимости от ваших имен файлов, вам, возможно, повезло раньше, и это сработало, даже если в документации явно сказано, что это не должно работать. В предварительном просмотре Android-L это несоответствие, похоже, было исправлено.

person mstorsjo    schedule 03.09.2014
comment
В целом да, но почти все устройства поддерживают два ABI. В моем случае Nexus 5 поддерживает: CPU_ABI = armeabi-v7a и CPU_ABI2 = armeabi. Тот же механизм используется, например, для устройств Intel, где первичный ABI — x86, а вторичный — armeabi, поэтому мое приложение отлично работает и на устройствах Intel, хотя в папке x86 нет родной библиотеки. На мой взгляд, нет причин, по которым это не должно работать и на L-Preview. - person xsveda; 03.09.2014
comment
Да, но менеджер пакетов проверяет наличие библиотек во вторичном каталоге ABI только в том случае, если в первичном каталоге ABI их не обнаружено. В документации, на которую я ссылаюсь, говорится: служба диспетчера пакетов будет сканировать .apk и искать любую общую библиотеку в форме: lib/‹primary-abi›/lib‹name›.so [...] Если она найдена, то он копируется под $APPDIR/lib/lib<name>.so [...] Если ничего не найдено и определен вторичный ABI, служба затем просканирует общие библиотеки в форме: lib/‹secondary-abi›/lib‹name›.so. Поэтому, если первичный каталог ABI не пуст, он не будет проверять вторичный. - person mstorsjo; 03.09.2014
comment
Вот что я имел в виду в своем первоначальном ответе - он всегда извлекается только из одного каталога архитектуры, который может быть либо первичным, либо вторичным ABI. - person mstorsjo; 03.09.2014
comment
Я протестировал его на Kitkat и эмуляторе L-Preview, а на Kitkat libFirstLibrary.so берется из папки armeabi-v7a, а libSecondLibrary.so берется из папки armeabi. Это больше не работает в L-Preview, где обе библиотеки должны находиться в папке armeabi-v7a или только в папке armeabi. Текст документации не на 100% ясен для этого, но определенно есть разница в поведении между Kitkat и L-Preview. @mstorsjo Пожалуйста, обновите свой ответ этой информацией, и я приму ее, спасибо за вашу помощь! - person xsveda; 04.09.2014
comment
Я был совершенно уверен, что старые версии Android не устанавливали библиотеки более чем из одного подкаталога архитектуры, но я провел тест и обнаружил, что это возможно, но не всегда. Поэтому мне кажется, что это была ошибка в старой версии, из-за которой иногда их можно было установить (в зависимости от имен файлов и порядка файлов в APK), и эта ошибка была устранена в android-L. Я обновлю ответ этой информацией. - person mstorsjo; 04.09.2014
comment
Кроме того, я думаю, что смешивание библиотек между первичным и вторичным ABI в любом случае может быть проблематичным. Рассмотрим случай, когда первичным является x86, а вторичным - armeabi. Я не знаком с эмуляцией руки на устройствах Android с архитектурой x86, но могу предположить, что для этого потребуется, чтобы весь процесс выполнялся в режиме руки. Но в любом случае тот факт, что это работало в более ранних версиях, кажется ошибкой. - person mstorsjo; 04.09.2014
comment
Случай с устройством x86 интересен, поэтому я попробовал его, и он работает на Kitkat так же, как и на устройствах ARM. - person xsveda; 04.09.2014