在我的情况下,System.loadLibrary(...)找不到本机库

时间:2014-12-11 10:54:39

标签: android android-ndk

我想使用来自另一个 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 /文件夹,并在jni /下添加一个Android.mk文件。 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"

13 个答案:

答案 0 :(得分:150)

为了根本原因(也许可以在同一时间解决您的问题),您可以执行以下操作:

  1. 删除jni文件夹和所有 .mk 文件。如果你没有编译任何东西,你就不需要这些也不需要NDK。

  2. libcalculate.so文件复制到<project>/libs/(armeabi|armeabi-v7a|x86|...)内。在使用Android Studio时,它是<project>/app/src/main/jniLibs/(armeabi|armeabi-v7a|x86|...),但我看到你正在使用eclipse。

  3. 构建您的APK并将其作为 zip文件打开,以检查您的libcalculate.so文件是否在 lib /(armeabi | armeabi-v7a | x86 | ...)

  4. 删除并安装您的应用程序

  5. 运行 dumpsys包软件包| grep yourpackagename 以获取应用程序的 nativeLibraryPath legacyNativeLibraryDir

  6. nativeLibraryDir / armeabi 上的 nativeLibraryPath 上运行 ls ,检查你的libcalculate.so是否确实存在

  7. 如果有,请检查它是否已从原始 libcalculate.so 文件中更改:是否针对正确的体系结构进行了编译,是否包含预期的符号,是否有任何遗漏的依赖。您可以使用readelf分析libcalculate.so。

  8. 为了检查步骤5-7,您可以使用我的应用程序而不是命令行和readelf:Native Libs Monitor

    PS:在默认情况下应该放置或生成.so文件的位置很容易混淆,这里有一个摘要:

      日食项目中的
    • libs / CPU_ABI

    • Android Studio项目中的
    • jniLibs / CPU_ABI

    • AAR内的
    • jni / CPU_ABI

    • 最终APK中的
    • lib / CPU_ABI

    • 在&lt; 5.0设备上的应用 nativeLibraryPath 内,以及&gt;上应用的 legacyNativeLibraryDir / CPU_ARCH 内部= 5.0设备。

    CPU_ABI 的任何一个: armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,mips,mips64 。取决于您针对哪些体系结构以及编译的库。

    另请注意,libs不会在CPU_ABI目录之间混合:您需要使用的全套内容, armeabi 文件夹中的lib将不会被安装如果APK中的 armeabi-v7a 文件夹中有任何库,则在 armeabi-v7a 设备上。

答案 1 :(得分:16)

在gradle中,将所有文件夹复制到libs/

jniLibs.srcDirs = ['libs']

将上述行添加到sourceSets文件中的build.gradle。没有其他任何工作。

答案 2 :(得分:12)

你正在使用gradle吗?如果是,请将.so文件放入<project>/src/main/jniLibs/armeabi/

我希望它有所帮助。

答案 3 :(得分:11)

在我的情况下,我必须通过gradle排除编译源并设置libs路径

android {

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

答案 4 :(得分:5)

出现此错误的原因是您的应用与您链接的本机库之间的ABI不匹配。换句话说,您的应用和public async Task<string> myFunction() { string back = "this is my code"; return back; } 针对的是不同的ABI。

如果您使用最新的Android Studio模板创建应用,则可能定位.so,但您的arm64-v8a可能会定位.so

有两种方法可以解决这个问题:

  1. 为您的应用支持的每个ABI构建您的本机库。
  2. 将您的应用更改为定位armeabi-v7a构建的旧版ABI。
  3. 选择2很脏,但我想你可能更感兴趣:

    更改您的应用.so

    build.gradle

答案 5 :(得分:4)

尝试在加入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目录中部署库。

答案 6 :(得分:4)

作为参考,我有这个错误消息,解决方案是当你指定库时,你会错过前面的'lib'和最后的'.so'。

因此,如果你有一个文件libmyfablib.so,你需要调用:

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

看了apk,安装/卸载并尝试了各种复杂的解决方案后,我无法看到面前的简单问题!

答案 7 :(得分:4)

这是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 - &gt; 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;
}

答案 8 :(得分:1)

您可以更改ABI以使用旧版本:

defaultConfig {
    ...

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

您还应该通过将以下行添加到gradle.properties中来使用已弃用的NDK:

android.useDeprecatedNdk=true

答案 9 :(得分:0)

请添加所有支持

应用程序/的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

答案 10 :(得分:0)

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

只需将这些行添加到build.gradle应用程序级别

答案 11 :(得分:-1)

实际上,您不能只在/libs/armeabi/中添加.so文件并使用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 documentation about prebuilt

答案 12 :(得分:-1)

根据我的经验,在armeabi-v7a手机中,当apk中存在armeabi和armeabi-v7a目录时,armeabi目录中的.so文件不会被链接,尽管armeabi中的.so文件如果armeabi-v7a不存在,将链接在相同的armeabi-v7a手机中。