一、开发步骤:
- 编写带有
native声明的方法的Java类 - 使用
javac命令编译编写的Java类得到class文件,如:javac NativeTest.java - 使用
javah -jni ****来生成后缀名为.h的头文件,如:javah -classpath D:\Study\idea\IdeaProjects\demo\target\classes -jni bytecode.NativeTest - 使用其他语言(
C、C++)实现本地方法 - 将本地方法编写的文件生成动态链接库(
windows下是.dll文件,Linux下是.so文件)
要特别注意第 2 步,使用 javah 生成 .h 文件时,正确格式为:javah -classpath .class文件所在目录(不含包) -jni 完整类名,这个 “ .class 文件所在目录(不含包) ” 既可以用绝对目录也可以用相对目录,.h 文件生成后位于执行 javah 命令时所在的目录
二、示例:
- 编写含有
native方法的Java源文件NativeTest.java
// Java 程序中调用 native 方法前在 VM Options 中设置 .dll 文件的绝对路径
-Djava.library.path=D:\Study\idea\IdeaProjects\demo\src\main\java\bytecode
package bytecode;
public class NativeTest {
public native void say();
public native String sayWithMsg(String msg);
// 静态 native 方法
public static native int sayWithMsgAndNum(String msg, int num);
static {
// 参数值是动态链接库名称,不要求必须和类名一致
System.loadLibrary("NativeTest");
}
public static void main(String[] args) {
NativeTest nativeTest = new NativeTest();
nativeTest.say();
System.out.println(nativeTest.sayWithMsg("java"));
System.out.println(sayWithMsgAndNum("java", 100));
}
}
-
编译
Java源文件生成.class文件NativeTest.class -
生成
.h头文件bytecode_NativeTest.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class bytecode_NativeTest */
#ifndef _Included_bytecode_NativeTest
#define _Included_bytecode_NativeTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: bytecode_NativeTest
* Method: say
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_bytecode_NativeTest_say
(JNIEnv *, jobject);
/*
* Class: bytecode_NativeTest
* Method: sayWithMsg
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_bytecode_NativeTest_sayWithMsg
(JNIEnv *, jobject, jstring);
/*
* Class: bytecode_NativeTest
* Method: sayWithMsgAndNum
* Signature: (Ljava/lang/String;I)I
*/
JNIEXPORT jint JNICALL Java_bytecode_NativeTest_sayWithMsgAndNum
(JNIEnv *, jclass, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif
这个 .h 文件可以这样理解:
其中最关键的就是定义了几个 Java_~ 方法,它们和 Java 代码中的方法一一对应,可以把 .h 文件类比 Java 里的接口,只定义不实现,然后我们在本地方法里面实现这些方法,也就是说我们在编写 C/C++ 程序的时候实现这些方法。
- 实现本地方法
新建一个CLion项目NativeTest,将前面生成的bytecode_NativeTest.h拷贝到项目中,然后将 Java 目录下的jni.h和jni_md.h文件拷贝至NativeTest项目里。
C:\Program Files\Java\jdk1.8.0_231\include\jni.h
C:\Program Files\Java\jdk1.8.0_231\include\win32\jni_md.h
拷贝后将bytecode_NativeTest.h文件中的#include <jni.h>改成#include "jni.h"
创建NativeTestImpl.c实现 JNI 方法
#include "bytecode_NativeTest.h"
JNIEXPORT void JNICALL Java_bytecode_NativeTest_say(JNIEnv *jniEnv, jobject obj) {
printf("a\n");
}
JNIEXPORT jstring JNICALL Java_bytecode_NativeTest_sayWithMsg(JNIEnv *jniEnv, jobject obj, jstring msg) {
printf("b\n");
return msg;
}
JNIEXPORT jint JNICALL Java_bytecode_NativeTest_sayWithMsgAndNum(JNIEnv *jniEnv, jobject obj, jstring msg, jint num) {
printf("c\n");
return num + 500;
}
注意直接编译会有问题:
cygwin jni 报错 '__int64' does not name a type error: 'jlong' does not name a type
因为cygwin 下 gnu 是不带 __int64 这个宏的。
所以需要在 jni_md.h 修改 __int64
// 原文件
typedef __int64 jlong;
// 修改为
#ifdef __GNUC__
typedef long long jlong;
#else
typedef __int64 jlong;
#endif
- 生成动态链接库
将NativeTestImpl.cpp源文件编译为NativeTest.dll动态链接库文件,并需要链接两个文件:
jni.h在JDK的include/目录下,jni_md.h在JDK的include/win32/目录下
如下-I参数是指定链接的路径:
// Linux 下使用
gcc -shared -I"C:\Program Files\Java\jdk1.8.0_231\include" -I"C:\Program Files\Java\jdk1.8.0_231\include\win32" NativeTestImpl.c -o NativeTest.dll
// Windows 下用这个,否则调用 native 方法时会报错
x86_64-w64-mingw32-gcc -shared -I"C:\Program Files\Java\jdk1.8.0_231\include" -I"C:\Program Files\Java\jdk1.8.0_231\include\win32" NativeTestImpl.c -o NativeTest.dll
然后将生成的 NativeTest.dll 动态链接库拷贝到和 NativeTest.java 文件相同的包下,就可以在 Java 程序中正确调用 native 方法了。














网友评论