System.loadLibrary(...) не смог найти нативную библиотеку в моем случае

Я хочу использовать существующую нативную библиотеку из другого проекта Android, поэтому я просто скопировал встроенную библиотеку NDK (libcalculate.so) в мой новый проект Android. В моем новом проекте Android я создал папку libs/armeabi/ и поместите libcalculate.so там. Там нет jni / папка. Мое тестирующее устройство имеет архитектуру ARM.

В моем Java-коде я загружаю библиотеку:

  static{
    System.loadLibrary("calculate");
  }

Когда я запускаю свой новый проект Android, я получил ошибку:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"

Итак, как говорит ошибка, скопированная нативная библиотека не находится в / verdor / lib или / system / lib, как решить эту проблему в моем случае?

(Я разархивировал пакет apk, в lib / есть libcalculate.so)

==== =====UPDATE

Я также попытался создать папку jni / в корневом каталоге проекта и добавить файл Android.mk в папку jni /. Содержание Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Затем в корне проекта я выполнил ndk-build . После этого каталоги armeabi / и armeabi-v7a/ генерируются с помощью ndk-build (с libcalculate.so внутри папки).

Затем я успешно запустил maven для сборки проекта. В окончательном пакете apk есть:

lib/armeabi/libcalculate.so
lib/armeabi-v7a/libcalculate.so

Но когда я запускаю свое приложение, выдается та же ошибка:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"

14 ответов

Решение

Чтобы устранить причину (и, возможно, решить вашу проблему в то же время), вот что вы можете сделать:

  1. Удалите папку jni и все файлы .mk. Вам не нужны ни эти, ни NDK, если вы ничего не компилируете.

  2. Скопируйте свой libcalculate.so файл внутри <project>/libs/(armeabi|armeabi-v7a|x86|...), При использовании Android Studio это <project>/app/src/main/jniLibs/(armeabi|armeabi-v7a|x86|...) Но я вижу, вы используете затмение.

  3. Создайте свой APK и откройте его в виде zip-файла, чтобы убедиться, что ваш libcalculate.so файл находится внутри lib / (armeabi | armeabi-v7a | x86 |...).

  4. Удалить и установить приложение

  5. Запустить пакетные пакеты dumpsys | grep yourpackagename, чтобы получить nativeLibraryPath или legacyNativeLibraryDir вашего приложения.

  6. Запустите ls на имеющейся у вас nativeLibraryPath или на legacyNativeLibraryDir / armeabi, чтобы проверить, действительно ли там есть ваш libcalculate.so.

  7. Если он есть, проверьте, не был ли он изменен из вашего исходного файла libcalculate.so: скомпилирован ли он с правильной архитектурой, содержит ли он ожидаемые символы, есть ли пропущенные зависимости. Вы можете проанализировать libcalculate.so, используя readelf.

Чтобы проверить шаги 5-7, вы можете использовать мое приложение вместо командной строки и readelf: Native Libs Monitor

PS: легко запутаться в том, где.so файлы должны быть помещены или сгенерированы по умолчанию, вот краткая информация:

  • libs /CPU_ABI внутри проекта Eclipse

  • jniLibs/CPU_ABI внутри проекта Android Studio

  • JNI /CPU_ABI внутри AAR

  • lib /CPU_ABI внутри финального APK

  • внутри nativeLibraryPath приложения на устройстве <5.0 и внутри legacyNativeLibraryDir/CPU_ARCH приложения на устройстве>=5.0.

Где CPU_ABI - любой из: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64. В зависимости от того, на какую архитектуру вы нацеливаетесь и какие ваши библиотеки были скомпилированы.

Также обратите внимание, что библиотеки lib не смешиваются между каталогами CPU_ABI: вам нужен полный набор того, что вы используете, библиотека, находящаяся внутри папки armeabi, не будет установлена ​​на устройстве armeabi-v7a, если внутри armeabi есть какие-либо библиотеки -v7a папка из APK.

В Gradle, после копирования всех файлов папок в libs/

jniLibs.srcDirs = ['libs']

Добавление вышеуказанной строки в sourceSets в build.gradle файл сработал. Ничто другое не сработало вообще.

Причиной этой ошибки является несоответствие ABI между вашим приложением и собственной библиотекой, с которой вы связаны. Другими словами, ваше приложение и ваш .so ориентируется на разные ABI.

если вы создаете свое приложение с использованием новейших шаблонов Android Studio, оно, вероятно, нацелено на arm64-v8a но твой .so может быть целью armeabi-v7a например.

Есть 2 способа решить эту проблему:

  1. создавайте свои собственные библиотеки для каждого ABI, поддерживаемого вашим приложением.
  2. изменить свое приложение, чтобы ориентироваться на более старый ABI, что ваш .so построен против.

Вариант 2 грязный, но я думаю, что вы, вероятно, больше заинтересованы в:

изменить ваше приложение build.gradle

android {
    defaultConfig {
        ...
        ndk {
            abiFilters 'armeabi-v7a'
        }
   }
}

В моем случае я должен исключить компиляцию исходников по gradle и установить путь libs

android {

    ...
    sourceSets {
        ...
        main.jni.srcDirs = []
        main.jniLibs.srcDirs = ['libs']
    }
....

Вы используете Gradle? Если это так, поставьте .so файл в <project>/src/main/jniLibs/armeabi/

Я надеюсь, что это помогает.

Для справки у меня было это сообщение об ошибке, и решение было таким: когда вы указываете библиотеку, вы пропускаете "lib" с начала и ".so" с конца.

Итак, если у вас есть файл libmyfablib.so, вам нужно вызвать:

   System.loadLibrary("myfablib"); // this loads the file 'libmyfablib.so' 

Посмотрев в апк, установил / удалил и перепробовал всевозможные сложные решения, я не увидел простую проблему, которая была прямо перед моим лицом!

Это обновление для Android 8.

В более ранней версии Android для собственных библиотек LoadLibrary (например, для доступа через JNI) я встроил свой собственный код для перебора ряда возможных путей к каталогам для папки lib на основе различных алгоритмов установки / обновления apk:

/data/data/<PackageName>/lib
/data/app-lib/<PackageName>-1/lib
/data/app-lib/<PackageName>-2/lib
/data/app/<PackageName>-1/lib
/data/app/<PackageName>-2/lib

Этот подход хоккейный и не будет работать для Android 8; из https://developer.android.com/about/versions/oreo/android-8.0-changes.html вы увидите, что в качестве части их изменений в "безопасности" вам теперь нужно использовать sourceDir:

"Вы больше не можете предполагать, что APK находятся в каталогах, имена которых заканчиваются на -1 или -2. Приложения должны использовать sourceDir для получения каталога, а не полагаться на формат каталога напрямую".

Исправление: sourceDir - это не способ найти ваши собственные общие библиотеки; использовать что-то вроде Протестировано для Android 4.4.4 -> 8.0

// Return Full path to the directory where native JNI libraries are stored.
private static String getNativeLibraryDir(Context context) {
    ApplicationInfo appInfo = context.getApplicationInfo();
    return appInfo.nativeLibraryDir;
}

Попробуйте позвонить в вашу библиотеку после включения PREBUILT_SHARED_LIBRARY раздел:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

#...

LOCAL_SHARED_LIBRARIES += libcalculate

Обновить:

Если вы будете использовать эту библиотеку в Java, вам нужно скомпилировать ее как общую библиотеку

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(BUILD_SHARED_LIBRARY)

И вам нужно развернуть библиотеку в /vendor/lib каталог.

Вы можете просто изменить ABI, чтобы использовать более старые сборки:

defaultConfig {
    ...

    ndk {
        abiFilters 'armeabi-v7a'
    }
    ...
}

Вы также должны использовать устаревший NDK, добавив эту строку в gradle.properties:

android.useDeprecatedNdk=true

На самом деле, вы не можете просто положить.so файл в /libs/armeabi/ и загрузить его System.loadLibrary, Вам нужно создать файл Android.mk и объявить готовый модуль, в котором вы указываете свой файл.so в качестве источника.

Для этого поместите файл.so и файл Android.mk в jni папка. Ваш Android.mk должен выглядеть примерно так:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Источник: Android NDK документация о сборке

      defaultConfig {
ndk {
            abiFilters "armeabi-v7a", "x86", "armeabi", "mips"
    }
}

Просто добавьте эту строку на уровень приложения build.gradle

Пожалуйста, добавьте все поддержки

Приложение /build.gradle

ndk {
        moduleName "serial_port"
        ldLibs "log", "z", "m"
        abiFilters "arm64-v8a","armeabi", "armeabi-v7a", "x86","x86_64","mips","mips64"
}

Приложение \ SRC \ JNI \Application.mk

APP_ABI := arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64

В моем случае я просто удаляю неиспользуемые общие библиотеки, после чего приложение сможет найти общую библиотеку. В частности, я удаляю jniLibs/arm64-v8a/ и libs/arm64-v8a/, которые использовались в приложении ранее, и оставляю только jniLibs/armeabi-v7a и libs/armeabi-v7a, которые должны использоваться приложением.

По моему опыту, на мобильном устройстве armeabi-v7a, когда в apk присутствуют каталоги armeabi и armeabi-v7a, файлы.so в каталоге armeabi не будут связаны, хотя файлы.so в armeabi будут связаны в тот же armeabi-v7a мобильный, если armeabi-v7a нет.

Другие вопросы по тегам