在Android中使用JNI:UNsatisfiedLinkError

7

我是jni的新手,正在学习如何实现一个简单的本地方法,但是我遇到了unsatisfiedlinkerror错误。据我所知,我完全按照教程的步骤进行操作。请帮助我。

以下是java包装器代码:

package com.cookbook.jni;

public class SquaredWrapper {

    // Declare native method (and make it public to expose it directly)
    public static native int squared(int base);

   // Provide additional functionality, that "extends" the native method
   public static int to4(int base)
   {
      int sq = squared(base);
      return squared(sq);
   }

   // Load library
   static {
      System.loadLibrary("squared");
   }
}

这是我的Android.mk文件的内容:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := squared LOCAL_SRC_FILES := squared.c

include $(BUILD_SHARED_LIBRARY)

这是我的.c文件的内容:

#include "squared.h"
#include <jni.h>

JNIEXPORT jint JNICALL Java_org_edwards_1research_demo_jni_SquaredWrapper_squared
  (JNIEnv * je, jclass jc, jint base)
{
     return (base*base);
}

这是我的.h文件的样子:

enter code here/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cookbook_jni_SquaredWrapper */

#ifndef _Included_com_cookbook_jni_SquaredWrapper
#define _Included_com_cookbook_jni_SquaredWrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_cookbook_jni_SquaredWrapper
* Method:    squared
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_cookbook_jni_SquaredWrapper_squared
  (JNIEnv *, jclass, jint);

#ifdef __cplusplus
}
#endif
#endif
2个回答

8
您的JNI签名不匹配。在您的.c文件中进行更改:

JNIEXPORT jint JNICALL Java_org_edwards_1research_demo_jni_SquaredWrapper_squared

to

JNIEXPORT jint JNICALL Java_com_cookbook_jni_SquaredWrapper_squared

通常有两种方法来通过JNI将本地C代码与Java函数“粘合”起来。第一种是您在此处尝试的方法,即使用JNI将识别并与适当的Java代码相关联的预定签名。第二种方法是在包含库时将函数指针、签名和Java类名称传递到JNI中。

这是第二种方法,它将本地函数绑定到适当的Java代码(这将是您的.c文件):

#include "squared.h"
#include <jni.h>

static const char* SquaredWrapper = "com/cookbook/jni/SquaredWrapper";

jint squared(JNIEnv * env, jobject this, jint base) {
     return (base*base);
}

// Methods to register for SquaredWrapper
static JNINativeMethod SquareWrapperMethods[] = {
        {"squared", "(I)I", squared}
};

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if ( (*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK)
        return JNI_ERR;

    jclass class = (*env)->FindClass(env, SquaredWrapper);
    (*env)->RegisterNatives(env, class, SquaredWrapperMethods, sizeof(SquaredWrapperMethods)/sizeof(SquaredWrapperMethods[0]));

    return JNI_VERSION_1_6;
}

void JNI_OnUnload(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
        return;

    jclass class = (*env)->FindClass(env, SquaredWrapper);
    (*env)->UnregisterNatives(env, class);

    return;

}

它更长,但在绑定本地代码时提供了更多的灵活性。 squared的定义和包含文件都是您所期望的。在第4行中,static const char * SquaredWrapper是一个转义字符串,其中包含您要将squared绑定到的类的完全限定包名称。底部附近是JNI_OnLoad和JNI_OnUnLoad函数,它们负责在库加载和卸载时绑定和解除绑定函数。最后一部分是JNINativeMethod数组。该数组每个条目都包含大小为3的数组,其组件是Java方法的名称(作为const char *),Java方法的JNI签名以及要绑定到该方法的本机C函数指针。 JNI函数签名告诉环境Java函数的参数列表格式和返回值。格式为“(Arg1Arg2Arg3 ...)Ret”,因此需要一个int和double并返回float的函数的签名为“(ID)F”,而不带参数并返回void的函数将是“()V”。我使用这个方便的速查表来记住大部分简写:http://dev.kanngard.net/Permalinks/ID_20050509144235.html祝你好运 :) 编辑:哦,顺便说一下,您可能需要将JNI_OnLoad和JNI_UnOnLoad的签名添加到头文件中,并更改本机函数原型的名称以反映新的.c文件。

非常感谢。我感觉很蠢。我只是从教程中复制并粘贴了C文件,但我使用的包名称与他不同。因此导致不匹配。我没有听说过第二种方法。你能否提供一个链接让我可以了解更多?再次感谢。 - user1487736
当然,我喜欢的方法是使用JNI_OnLoad:http://developer.android.com/guide/practices/jni.html#native_libraries 并保持我所有函数和签名的数组。让我写一些示例代码,然后我会编辑我的答案。 - AlcoJaguar

0
这是一种比较晦涩的情况,但如果你在本地代码中遇到了访问冲突,Android 就会掩盖抛出的异常并抛出你遇到的错误。在我的情况下,本机代码抛出了访问违规行为,但 Java 仍在运行。然后它尝试在崩溃的 NDK 上调用 JNI 方法。
为了找到访问冲突,我最终将有问题的 JNI 方法移动到另一个 IDE 进行调试。
我希望这能让其他人节省我用来解决这个问题的时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接