jni 1-java中使用本地方法

java本身提供了jni(Java Native Interface) ,使得java可以与其他语言的代码(主要是c/c++)。

其实jni相对来说还是比较简单的,举一个简单的例子:

新建一个HelloNative.java

class HelloNative
{
	public static native void greeting();
}

然后在控制台下,并设置当前目录为工作目录(cd或者按住shift并点击右键打开命令窗口),输入命令javah HelloNative,会在当前目录下生成一个对应的头文件HelloNative.h

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

#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloNative
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

这里可以把函数名括号前的几个空格去掉,免得在编译动态库时出错。

之后再编写对应的实现函数文件HelloNative.c

#include <stdio.h>

#include "HelloNative.h"

JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv *, jclass)
{
	printf("Hello Native World\n");
}

然后可以使用各种工具编译上述代码并生成动态库,比较简单的就是使用vs,让vs生成对应的动态库,先创建一个空项目,导入对应的文件,比如本例中,有HelloNative.h HelloNative.c jni.h jni_md.h。



注意这里的平台是64位,因为我的java 是64位的。32位和64位的区别,大家可以看看这个帖子:

https://blog.csdn.net/woainishifu/article/details/54017550

这里注意一下jni.h(从jdk下的include) 和jni_md.h(include/win32下)。

然后生成的dll文件放到对应的java文件夹中,之后就开始测试:

HelloNativeTest.java

class HelloNativeTest
{
	public static void main(String[] args)
	{
		HelloNative.greeting();
	}
	static
	{
		System.loadLibrary("HelloNative");
	}
}

代码不算多,程序运行时,先执行对应的静态代码块,加载进来对应的HelloNative库,然后在主函数中调用对应的函数。

运行无误的话,控制台会有以下输出:


另外注意,java中的类型是与平台无关的,而c/c++的数据类型是平台相关的。为此,java为了保证一致性,在声明native方法时,需要使用jni文件中的类型。

java c/c++ 字节
boolean jboolean 1
byte jbyte 1
char jchar 2
short jshort 2
int jint 4
long jlong 8
float jfloat 4
double jdouble 8

再在HelloNative类中新添加一个方法:

class HelloNative
{
	public static native void greeting();
	public static native int print(int width, int precision, double x);
}

然后javah生成对应的头文件 HelloNative.h

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

#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloNative
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv *, jclass);

/*
 * Class:     HelloNative
 * Method:    print
 * Signature: (IID)I
 */
JNIEXPORT jint JNICALL Java_HelloNative_print(JNIEnv *, jclass, jint, jint, jdouble);

#ifdef __cplusplus
}
#endif
#endif

可以看到,方法后面的参数类型未jint,jint,jdouble。

然后进行对应的实现:

JNIEXPORT jint JNICALL Java_HelloNative_print(JNIEnv * env, jclass cl, jint width, jint precision, jdouble x)
{
	char fmt[30];
	int ret = JNI_FALSE;

	memset(fmt, '\0', sizeof(fmt));
	//"%width.precision f"
	sprintf(fmt, "%%%d.%df", width, precision);
	ret = printf(fmt, x);

	fflush(stdout);
	return ret;
}

这里先是给fmt数组进行初始化,然后输出。我在使用sprintf时,可能会出错


在vs中添加对应的宏即可:


之后编译即可。不过vs提供了对应的安全的sprintf_s函数,虽然我动态库编译成功,但是我在运行java函数会报错:


不知道sprintf_s内部使用了什么造成这个错误。。。

猜你喜欢

转载自blog.csdn.net/bull521/article/details/80105316