Android L Preview не търси собствени библиотеки в папка armeabi (UnsatisfiedLinkError)

Имам приложение с 2 родни библиотеки. Първият работи много по-бързо на 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 MB по-голям, което не ми харесва много.

Това просто грешка на 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 не инсталират libs от повече от една архитектурна поддиректория, но направих тест и разбрах, че може, но не винаги. Така че за мен изглежда, че това е грешка в по-старата версия, която понякога може да ги инсталира (в зависимост от имената на файловете и реда на файловете в APK), и тази грешка е разрешена в android-L. Ще актуализирам отговора с тази информация. - person mstorsjo; 04.09.2014
comment
Също така - мисля, че смесването на библиотеки между първичния и вторичния ABI така или иначе може да бъде проблематично. Помислете за случая, когато първичният е x86, а вторичният е armeabi - не съм запознат с емулацията на arm на x86 android устройства, но предполагам, че ще изисква целият процес да работи в режим на arm. Но както и да е, фактът, че това изглежда работи на по-ранни версии, просто изглежда е грешка. - person mstorsjo; 04.09.2014
comment
Случаят с устройството x86 е интересен, така че го пробвах и работи на Kitkat по същия начин като ARM устройствата. - person xsveda; 04.09.2014