JNI学习笔记,含VS配置x64平台的问题
1、来个java程序,本地方法前加native关键字
public class HelloWorld_20111226 {public static native void printHelloWorld();public static native int add(int a, int b);static {System.loadLibrary("TestJNI_201112");}public static void main(String[] args) {printHelloWorld();int sum = add(1, 2);System.out.println(sum);}}
2.生成C++头文件
命令行下 javah 类名(在.class的根路径下 报名.类名,无需后缀)
简单方法见http://cherishlc.iteye.com/blog/1326893
生成的头文件如下
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class HelloWorld_20111226 */#ifndef _Included_HelloWorld_20111226#define _Included_HelloWorld_20111226#ifdef __cplusplusextern "C" {#endif/* * Class: HelloWorld_20111226 * Method: printHelloWorld * Signature: ()V */JNIEXPORT void JNICALL Java_HelloWorld_120111226_printHelloWorld (JNIEnv *, jclass);/* * Class: HelloWorld_20111226 * Method: add * Signature: (II)I */JNIEXPORT jint JNICALL Java_HelloWorld_120111226_add (JNIEnv *, jclass, jint, jint);#ifdef __cplusplus}#endif#endif
3.写C++端的实现
新建vs的win32工程:

接下来选择DLL工程:

加入刚刚生成的头文件;写实现函数的cpp文件,写导出函数的.def文件:

#include "HelloWorld_20111226.h"#include <iostream>JNIEXPORT void JNICALL Java_HelloWorld_120111226_printHelloWorld( JNIEnv *, jclass ){std::cout<<"Hello world"<<std::endl;}JNIEXPORT jint JNICALL Java_HelloWorld_120111226_add( JNIEnv *, jclass, jint a, jint b){return a+b;}
注意:64位的JVM需要64位的DLL!!
设置方法: VS下设置如下:
点击工具栏上的配置编译的平台的工具栏的向下的箭头:

选择所需工程,点击之:

选择x64架构,点ok,搞定!!
注意:jni.h的路径要加进来,你懂的:

注意:还是老老实实用.def文件定义导出函数吧!不这样函数名可能会被编译器改掉!!!
//main.def文件如下:LIBRARY "TestJNI_201112"EXPORTSJava_HelloWorld_120111226_printHelloWorldJava_HelloWorld_120111226_addJava_LC_TestJNI_printIntJava_LC_TestJNI_printIntArray
4.编译,将生成的dll文件考到当前路径下(在Eclipse中运行即为工程文件所在路径),运行java程序,一切OK
注意:别忘了java类里要加载DLL“static {System.loadLibrary("TestJNI_201112");}”! 不带后缀!
5.进阶:
Jni中C++和Java的参数传递http://www.blogjava.net/china-qd/archive/2006/04/29/44002.html
Java Native Interface Specification:http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
6.在Java中用C++的类:
只是想到一个小技巧:
声明一个所需类的全局指针变量,需要时new一下, 不需要时delete之;缺点是只能用一个啊! 当然用个vector之类的记录当前活动的自然可以,可是多了一层,速度受影响吧!!
部分代码如下:
#include "Maxflow/LCEnergyMinimize_Graph.h"#include "GraphMinimization_GraphCut.h"typedef LCEnergyMinimize_Graph<jint,jlong> GraphIntLong;GraphIntLong* g;JNIEXPORT void JNICALL Java_GraphMinimization_GraphCut_generateGraph( JNIEnv *, jobject ){if(g!=NULL)delete g;g=new GraphIntLong();}/* * Class: GraphMinimization_GraphCut * Method: releaseGraph * Signature: ()V */JNIEXPORT void JNICALL Java_GraphMinimization_GraphCut_releaseGraph (JNIEnv *, jobject){ if(g!=NULL)delete g; g=NULL;}/* * Class: GraphMinimization_GraphCut * Method: addVariable * Signature: (I)I */JNIEXPORT jint JNICALL Java_GraphMinimization_GraphCut_addVariable (JNIEnv *, jobject, jint num){ return g->addVariable(num);}
7.调试记录:
一开始程序经常崩溃,只说是C++端的错误。
后来看到下面的error log,至少知道是Graph<long,long,__int64>::add_edge出错,后来发现其中有assert语句,检查下标越界,感觉是自己写的时候下标越界了,在java里调用add_edge的地方增添了句检查下标越界的操作,越界时抛出异常:throw new RuntimeException("x = "+x+"\ty = "+y+"\t varNum = "+getVarNum());
果然问题出在下标上!!!
后来发现自己存不同下标的变量太多,有一处弄混了!!
Stack: [0x0000000001ca0000,0x0000000001da0000], sp=0x0000000001d9f750, free space=1021kNative frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)C [MaxflowJNIInterface.dll+0x1a74] Graph<long,long,__int64>::add_edge+0x74Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)J GraphMinimization.GraphCut.addTerm2(IIIIII)VJ app.StereoCorrespodenceUsingGraphCut.generateStereoCorrespodenceGraph(Llc/util/image/IntPair;)LGraphMinimization/GraphCut;j app.StereoCorrespodenceUsingGraphCut.calculate()V+52j app.StereoCorrespodenceUsingGraphCut.main([Ljava/lang/String;)V+62v ~StubRoutines::call_stub