JNI java.lang.UnsatisfiedLinkError
使用VS2010编译JNI,开始时Java端调用正常,加入几个函数后,怎么都无法调用了,报如下错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.mingspy.jseg.JSegJNI.MaxSplit(Ljava/lang/String;)Ljava/util/List;
在网上查询了一下,可能是jni端函数名字编译时,多了字符@,需要加参数。查询了不少文章,只是用于gcc的(如加入在linker flags处填入:-Wl,--add-stdcall-alias),唯独没有关于VS的。
最后采用RegisterNativeMethods解决。摘自网上的一段评论如下:
除了使用传统方法实现JNI外,也可以使用RegisterNatives实现JNI。和传统方法相比,使用RegisterNatives的好处有三点:
1、C++中函数命名自由,不必像javah自动生成的函数声明那样,拘泥特定的命名方式;
2、效率高。传统方式下,Java类call本地函数时,通常是依靠VM去动态寻找.so中的本地函数(因此它们才需要特定规则的命名格式),而使用RegisterNatives将本地函数向VM进行登记,可以让其更有效率的找到函数;
3、运行时动态调整本地函数与Java函数值之间的映射关系,只需要多次call RegisterNatives()方法,并传入不同的映射表参数即可。
为了使用RegisterNatives,我们需要了解JNI_OnLoad和JNI_OnUnload函数。JNI_OnLoad()函数在VM执行System.loadLibrary(xxx)函数时被调用,它有两个重要的作用:
- 指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供 JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。
- 初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当,RegisterNatives也在这里进行。
JNI_OnUnload()当VM释放该组件时被调用,JNI_OnUnload()函数的作用与JNI_OnLoad()对应,因此在该方法中进行善后清理,资源释放的动作最为合适。
我的代码示例如下, 见JNI_OnLoad:
#include <iostream> //#include "JSegJNI.h" #include <jni.h> #include "AutoTokenizer.hpp" #include "CodeUtils.hpp" using namespace std; using namespace mingspy; static ITokenizer & GetTokenizer(){ static AutoTokenizer t; return t; } static jobject toJavaList(JNIEnv * env, const vector<Token> & result){ jclass list_cls = env->FindClass("Ljava/util/ArrayList;");//获得ArrayList类引用 jmethodID list_costruct = env->GetMethodID(list_cls , "<init>","()V"); //获得得构造函数Id jmethodID list_add = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z"); jobject list_obj = env->NewObject(list_cls , list_costruct); jclass token_cls = env->FindClass("Lcom/mingspy/jseg/Token;"); jmethodID token_costruct = env->GetMethodID(token_cls , "<init>", "(II)V"); for(int i = 0 ; i < result.size(); i++) { jobject t_obj = env->NewObject(token_cls , token_costruct , result[i]._off,result[i]._len); env->CallBooleanMethod(list_obj , list_add , t_obj); } return list_obj ; } /* * Class: com_mingspy_jseg_JSegJNI * Method: MaxSplit * Signature: (Ljava/lang/String;)Ljava/util/List; */ jobject Java_com_mingspy_jseg_JSegJNI_MaxSplit (JNIEnv * env, jobject obj, jstring jstr) { const char * p = env->GetStringUTFChars(jstr, 0); vector<Token> result; GetTokenizer().maxSplit(Utf8ToUnicode(p), result); env->ReleaseStringUTFChars(jstr, p); return toJavaList(env,result); } /* * Class: com_mingspy_jseg_JSegJNI * Method: FullSplit * Signature: (Ljava/lang/String;)Ljava/util/List; */ jobject Java_com_mingspy_jseg_JSegJNI_FullSplit (JNIEnv * env, jobject obj, jstring jstr) { const char * p = env->GetStringUTFChars(jstr, 0); vector<Token> result; GetTokenizer().fullSplit(Utf8ToUnicode(p), result); env->ReleaseStringUTFChars(jstr, p); return toJavaList(env,result); } /* * Class: com_mingspy_jseg_JSegJNI * Method: UniGramSplit * Signature: (Ljava/lang/String;)Ljava/util/List; */ jobject Java_com_mingspy_jseg_JSegJNI_UniGramSplit (JNIEnv * env, jobject obj, jstring jstr){ const char * p = env->GetStringUTFChars(jstr, 0); vector<Token> result; GetTokenizer().uniGramSplit(Utf8ToUnicode(p), result); env->ReleaseStringUTFChars(jstr, p); return toJavaList(env,result); } /* * Class: com_mingspy_jseg_JSegJNI * Method: BiGramSplit * Signature: (Ljava/lang/String;)Ljava/util/List; */ jobject Java_com_mingspy_jseg_JSegJNI_BiGramSplit (JNIEnv * env, jobject obj, jstring jstr){ const char * p = env->GetStringUTFChars(jstr, 0); vector<Token> result; GetTokenizer().biGramSplit(Utf8ToUnicode(p), result); env->ReleaseStringUTFChars(jstr, p); return toJavaList(env,result); } /* * Class: com_mingspy_jseg_JSegJNI * Method: MixSplit * Signature: (Ljava/lang/String;)Ljava/util/List; */ jobject Java_com_mingspy_jseg_JSegJNI_MixSplit (JNIEnv * env, jobject obj, jstring jstr) { const char * p = env->GetStringUTFChars(jstr, 0); vector<Token> result; GetTokenizer().mixSplit(Utf8ToUnicode(p), result); env->ReleaseStringUTFChars(jstr, p); return toJavaList(env,result); } /* * Class: com_mingspy_jseg_JSegJNI * Method: Test * Signature: (Ljava/lang/String;)V */ void Java_com_mingspy_jseg_JSegJNI_Test (JNIEnv * env, jobject obj, jstring jstr) { const char * p = env->GetStringUTFChars(jstr, 0); cout<<"input is:"<<p<<endl; vector<Token> result; GetTokenizer().maxSplit(Utf8ToUnicode(p), result); env->ReleaseStringUTFChars(jstr, p); for(int i = 0; i< result.size(); i++){ cout<<"("<<result[i]._off<<","<<result[i]._len<<")"; } } static JNINativeMethod s_methods[] = { {"MaxSplit", "(Ljava/lang/String;)Ljava/util/List;", (void*)Java_com_mingspy_jseg_JSegJNI_MaxSplit}, {"FullSplit", "(Ljava/lang/String;)Ljava/util/List;", (void*)Java_com_mingspy_jseg_JSegJNI_FullSplit}, {"UniGramSplit", "(Ljava/lang/String;)Ljava/util/List;", (void*)Java_com_mingspy_jseg_JSegJNI_UniGramSplit}, {"BiGramSplit", "(Ljava/lang/String;)Ljava/util/List;", (void*)Java_com_mingspy_jseg_JSegJNI_BiGramSplit}, {"MixSplit", "(Ljava/lang/String;)Ljava/util/List;", (void*)Java_com_mingspy_jseg_JSegJNI_MixSplit}, }; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv* env = NULL; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { return JNI_ERR; } jclass cls = env->FindClass("Lcom/mingspy/jseg/JSegJNI;"); if (cls == NULL) { return JNI_ERR; } int len = sizeof(s_methods) / sizeof(s_methods[0]); if (env->RegisterNatives(cls, s_methods, len) < 0) { return JNI_ERR; } return JNI_VERSION_1_4; }