package test;
public class Example {
public native void set(String info);
public native String get();
}
(1)点击eclipse工具栏外部工具按钮,打开配置外部工具;
(2)选中program右键点击添加;
(3)配置如下信息后点击run,生成的test_Example.h文件在项目的jni目录下。
在name框里输入命令的名字:javah
在location框里输入javah.exe的绝对路径: D:\jdk1.8\bin\javah.exe
在Working Directory框里输入:${project_loc}
在Arguments框里输入: -v-classpath "${project_loc}/bin" -d "${project_loc}/jni"-jni ${java_type_name}
(1)vs下新建DLL项目,将test_Example.h和jni.h(包含JAVA_HOME/include/jni.h和JAVA_HOME/include/win32/jni_md.h)添加进工程。
(2)新建dlltest.cpp实现test_Example.h的C++函数
#include
#include "opencv2/highgui/highgui.hpp"
#include "test_Example.h"
std::string s = "hello dll";
std::string jstring2str(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr,mid,strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr,JNI_FALSE);
if(alen > 0)
{
rtn = (char*)malloc(alen+1);
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
env->ReleaseByteArrayElements(barr,ba,0);
std::string stemp(rtn);
free(rtn);
return stemp;
}
jstring charTojstring(JNIEnv* env, const char* pat) {
//定义java String类 strClass
jclass strClass = (env)->FindClass("Ljava/lang/String;");
//获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = (env)->GetMethodID(strClass, "", "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray(strlen(pat));
//将char* 转换为byte数组
(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat);
// 设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = (env)->NewStringUTF("GB2312");
//将byte数组转换为java String,并输出
return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
}
JNIEXPORT void JNICALL Java_test_Example_set
(JNIEnv* env, jobject obj, jstring str) {
s = jstring2str(env,str);
std::cout<
(3)编译,生成dll文件
编写测试类,并将dlltest.dll文件放在classpath下。
package test;
public class Test {
static {
System.load("E:\\workspace\\test\\src\\dlltest.dll");
}
public static void main(String[] args) {
Example e = new Example();
System.out.println(e.get());
e.set("hello java");
}
}
运行结果:
hello dll
hello java
(1)找不到对应的dll,原因很可能是不在classpath下,使用绝对路径试试;
(2)dll的64位版本和Eclipse的32位版本的不一致错误;
(3)web下找不到dll可参考:http://blog.csdn.net/l1028386804/article/details/53903557
除了使用传统方法实现JNI外,也可以使用RegisterNatives实现JNI。和传统方法相比,使用RegisterNatives的好处有三点:
(1)C++中函数命名自由,不必像javah自动生成的函数声明那样,拘泥特定的命名方式;
(2)效率高。传统方式下,Java类调用本地函数时,通常是依靠VM去动态寻找.so中的本地函数(因此它们才需要特定规则的命名格式),而使用RegisterNatives将本地函数向VM进行登记,可以让其更有效率的找到函数;
(3)运行时动态调整本地函数与Java函数值之间的映射关系,只需要多次调用RegisterNatives()方法,并传入不同的映射表参数即可。
为了使用RegisterNatives,我们需要了解JNI_OnLoad和JNI_OnUnload函数。JNI_OnLoad()函数在VM执行System.loadLibrary(xxx)函数时被调用,它有两个重要的作用:(1)指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。(2)初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当,RegisterNatives也在这里进行。JNI_OnUnload()当VM释放该组件时被调用,JNI_OnUnload()函数的作用与JNI_OnLoad()对应,因此在该方法中进行善后清理,资源释放的动作最为合适。
相比传统方式,vs项目下有所不同。只需在dll项目下声明本地方法,并重载jni.h中的JNI_ONLOAD方法来注册本地方法
static JNINativeMethod methods[] = {
{"get", "()Ljava/lang/String;", reinterpret_cast(get)},
{"set", "(Ljava/lang/String;)V", reinterpret_cast(set)}
};
其中JNINativeMethod是jni.h中定义的结构体
/*
* used in RegisterNatives to describe native method name, signature,
* and function pointer.
*/
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
name是java中定义的native函数的名字,fnPtr是函数指针,也就是C++中java native函数的实现。signature是java native函数的签名,可以认为是参数和返回值。native函数签名参考下表,其实也可对照生成的javah文件中的注释复制就行。
字符 | java | C/C++类型 |
V | void | void |
Z | jboolean | boolean |
I | jint | int |
J | jlong | long |
D | jdouble | double |
F | jfloat | float |
B | jbyte | byte |
C | jchar | char |
S | jshort | short |
为了与JNINativeMethod的函数名对应,简化native方法名,同时应修改javah的函数声明:
JNIEXPORT void JNICALL set(JNIEnv *, jobject, jstring);
JNIEXPORT jstring JNICALL get (JNIEnv *, jobject);
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("test/Example");
if (cls == NULL)
{
return JNI_ERR;
}
int len = sizeof(methods) / sizeof(methods[0]);
if (env->RegisterNatives(cls, methods, len) < 0)
{
return JNI_ERR;
}
return JNI_VERSION_1_4;
}
生成dll后与前面的用法完全一样。
更多内容请参考:https://www.jianshu.com/p/216a41352fd8
http://blog.csdn.net/qiuxiaolong007/article/details/7860610