java - 示例 hello-jni 上的 Android NDK java.lang.UnsatisfiedLinkError

标签 java android android-studio android-ndk unsatisfiedlinkerror

我需要在 android 上运行一些 native C 代码,并尝试设置 NDK。我正在使用 Android studio。

我已经导入了 hello-jni 示例,但它根本不起作用。 我总是收到 java.lang.UnsatisfiedLinkError 错误。

ndk 构建:

$ ndk-build
[arm64-v8a] Gdbserver      : [aarch64-linux-android-4.9] libs/arm64-v8a/gdbserver
[arm64-v8a] Gdbsetup       : libs/arm64-v8a/gdb.setup
[x86_64] Gdbserver      : [x86_64-4.9] libs/x86_64/gdbserver
[x86_64] Gdbsetup       : libs/x86_64/gdb.setup
[mips64] Gdbserver      : [mips64el-linux-android-4.9] libs/mips64/gdbserver
[mips64] Gdbsetup       : libs/mips64/gdb.setup
[armeabi-v7a] Gdbserver      : [arm-linux-androideabi-4.8] libs/armeabi-v7a/gdbserver
[armeabi-v7a] Gdbsetup       : libs/armeabi-v7a/gdb.setup
[armeabi] Gdbserver      : [arm-linux-androideabi-4.8] libs/armeabi/gdbserver
[armeabi] Gdbsetup       : libs/armeabi/gdb.setup
[x86] Gdbserver      : [x86-4.8] libs/x86/gdbserver
[x86] Gdbsetup       : libs/x86/gdb.setup
[mips] Gdbserver      : [mipsel-linux-android-4.8] libs/mips/gdbserver
[mips] Gdbsetup       : libs/mips/gdb.setup
[arm64-v8a] Compile        : hello-jni <= hello-jni.c
[arm64-v8a] SharedLibrary  : libhello-jni.so
[arm64-v8a] Install        : libhello-jni.so => libs/arm64-v8a/libhello-jni.so
[x86_64] Compile        : hello-jni <= hello-jni.c
[x86_64] SharedLibrary  : libhello-jni.so
[x86_64] Install        : libhello-jni.so => libs/x86_64/libhello-jni.so
[mips64] Compile        : hello-jni <= hello-jni.c
[mips64] SharedLibrary  : libhello-jni.so
[mips64] Install        : libhello-jni.so => libs/mips64/libhello-jni.so
[armeabi-v7a] Compile thumb  : hello-jni <= hello-jni.c
[armeabi-v7a] SharedLibrary  : libhello-jni.so
[armeabi-v7a] Install        : libhello-jni.so => libs/armeabi-v7a/libhello-jni.so
[armeabi] Compile thumb  : hello-jni <= hello-jni.c
[armeabi] SharedLibrary  : libhello-jni.so
[armeabi] Install        : libhello-jni.so => libs/armeabi/libhello-jni.so
[x86] Compile        : hello-jni <= hello-jni.c
[x86] SharedLibrary  : libhello-jni.so
[x86] Install        : libhello-jni.so => libs/x86/libhello-jni.so
[mips] Compile        : hello-jni <= hello-jni.c
[mips] SharedLibrary  : libhello-jni.so
[mips] Install        : libhello-jni.so => libs/mips/libhello-jni.so

jni/Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

jni/hello-jni.c:

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
#if defined(__arm__)
  #if defined(__ARM_ARCH_7A__)
    #if defined(__ARM_NEON__)
      #if defined(__ARM_PCS_VFP)
        #define ABI "armeabi-v7a/NEON (hard-float)"
      #else
        #define ABI "armeabi-v7a/NEON"
      #endif
    #else
      #if defined(__ARM_PCS_VFP)
        #define ABI "armeabi-v7a (hard-float)"
      #else
        #define ABI "armeabi-v7a"
      #endif
    #endif
  #else
   #define ABI "armeabi"
  #endif
#elif defined(__i386__)
   #define ABI "x86"
#elif defined(__x86_64__)
   #define ABI "x86_64"
#elif defined(__mips64)  /* mips64el-* toolchain defines __mips__ too */
   #define ABI "mips64"
#elif defined(__mips__)
   #define ABI "mips"
#elif defined(__aarch64__)
   #define ABI "arm64-v8a"
#else
   #define ABI "unknown"
#endif

    return (*env)->NewStringUTF(env, "Hello from JNI !  Compiled with ABI " ABI ".");
}

com.example.hellojni.HelloJni:

public class HelloJni extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        /* Create a TextView and set its content.
         * the text is retrieved by calling a native
         * function.
         */
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }

    /* A native method that is implemented by the
     * 'hello-jni' native library, which is packaged
     * with this application.
     */
    public native String  stringFromJNI();

    /* This is another native method declaration that is *not*
     * implemented by 'hello-jni'. This is simply to show that
     * you can declare as many native methods in your Java code
     * as you want, their implementation is searched in the
     * currently loaded native libraries only the first time
     * you call them.
     *
     * Trying to call this function will result in a
     * java.lang.UnsatisfiedLinkError exception !
     */
    public native String  unimplementedStringFromJNI();

    /* this is used to load the 'hello-jni' library on application
     * startup. The library has already been unpacked into
     * /data/data/com.example.hellojni/lib/libhello-jni.so at
     * installation time by the package manager.
     */
    static {
        System.loadLibrary("hello-jni");
    }
}

错误:

11-13 22:38:40.227  19386-19386/com.example.hellojni E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.example.hellojni, PID: 19386
    java.lang.UnsatisfiedLinkError: Couldn't load hello-jni from loader dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.hellojni-7.apk"],nativeLibraryDirectories=[/data/app-lib/com.example.hellojni-7, /vendor/lib, /system/lib]]]: findLibrary returned null
            at java.lang.Runtime.loadLibrary(Runtime.java:358)
            at java.lang.System.loadLibrary(System.java:526)
            at com.example.hellojni.HelloJni.<clinit>(HelloJni.java:64)
            at java.lang.Class.newInstanceImpl(Native Method)
            at java.lang.Class.newInstance(Class.java:1208)
            at android.app.Instrumentation.newActivity(Instrumentation.java:1067)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2399)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2599)
            at android.app.ActivityThread.access$900(ActivityThread.java:174)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1321)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:146)
            at android.app.ActivityThread.main(ActivityThread.java:5748)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
            at dalvik.system.NativeStart.main(Native Method)

如果您知道是什么原因造成的,请帮助我。我已经在这上面浪费了一整天的时间了,准备跳出窗外(带着电脑)。

最佳答案

这是因为 Android Studio 会忽略您的 Android.mk 并即时生成自己的 Android.mk,从 gradle 脚本获取设置。因此,您应该在 gradle 脚本中指定正确的设置(由于 Android Studio 中的 NDK 支持不完整,该脚本仅在最简单的情况下有效),或者完全禁用 Android Studio 有限的 NDK 支持并直接从 gradle 调用 ndk-build。

要在 gradle 脚本中指定 NDK 相关设置,请添加以下部分:

defaultConfig {
    ...
    ndk {
        moduleName "my-module-name"
        cFlags "-std=c++11 -fexceptions"
        ldLibs "log"
        stl "gnustl_shared"
        abiFilter "armeabi-v7a"
    }
}

并在那里设置您的项目特定值。然后,Android Studio 的 gradle 插件将考虑这些设置,并使用这些设置自动生成 Android.mk。

但请注意,唯一可用的 NDK 选项是上面列出的选项。如果您有更复杂的 NDK 设置(例如,多个 NDK 模块,相互依赖,或者您想要使用预构建的库,或其他什么),您应该完全禁用 Android Studio 有限的 NDK 支持并直接从 gradle 调用 ndk-build。为此,首先禁用 Android.mk 自动生成:

android {
    .....
    // disable automatic ndk-build call, which ignore our Android.mk
    sourceSets.main.jni.srcDirs = []
}

然后添加以下行:

android {
    .....
    sourceSets.main.jniLibs.srcDir 'src/main/libs'

    // call regular ndk-build(.cmd) script from app directory
    task ndkBuild(type: Exec) {
        workingDir file('src/main')
        commandLine getNdkBuildCmd()
    }

    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn ndkBuild
    }

    task cleanNative(type: Exec) {
        workingDir file('src/main')
        commandLine getNdkBuildCmd(), 'clean'
    }

    clean.dependsOn cleanNative
}

最后,将辅助函数添加到 gradle 脚本的末尾:

def getNdkDir() {
    if (System.env.ANDROID_NDK_ROOT != null)
        return System.env.ANDROID_NDK_ROOT

    Properties properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())
    def ndkdir = properties.getProperty('ndk.dir', null)
    if (ndkdir == null)
        throw new GradleException("NDK location not found. Define location with ndk.dir in the local.properties file or with an ANDROID_NDK_ROOT environment variable.")

    return ndkdir
}

def getNdkBuildCmd() {
    def ndkbuild = getNdkDir() + "/ndk-build"
    if (Os.isFamily(Os.FAMILY_WINDOWS))
        ndkbuild += ".cmd"

    return ndkbuild
}

啊,别忘了在 gradle 脚本的开头添加“import”:

import org.apache.tools.ant.taskdefs.condition.Os

Here我已经描述得更详细了。

关于java - 示例 hello-jni 上的 Android NDK java.lang.UnsatisfiedLinkError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29609188/

相关文章:

java - 什么是NullPointerException,我该如何解决?

java - 如何将此 jReddit 库/API 添加到我的 eclipse 项目中?

android - Google Play 静默发布

android - Android 的 initrd.img 中包含什么内容?

Android Studio 2.3 不显示异常堆栈跟踪

Java8 Lambda 和异常

java - 如何使用泛型调用最具体的方法?

java - Agora Android 示例应用程序在启动时崩溃

Android Studio : How to quickly move styling from layout. xml 到 style.xml?

android - Mapbox 'setMyLocationEnabled' 不工作