下面看一个实例,如下:
public class TestJNI { static { System.loadLibrary("diaoyong"); // 程序在加载时,自动加载libdiaoyong.so库 } public native void set(int value); // 声明原生函数。注意要添加native关键字 public native int get(); public static void main(String[] args) { TestJNI test = new TestJNI(); test.set(1); System.out.println(test.get()); } }
javac TestJNI.java命令生成java.class文件
javah -jni TestJNI命令生成TestJNI.h文件,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class TestJNI */ #ifndef _Included_TestJNI #define _Included_TestJNI #ifdef __cplusplus extern "C" { #endif /* * Class: TestJNI * Method: set * Signature: (I)V */ JNIEXPORT void JNICALL Java_TestJNI_set (JNIEnv *, jobject, jint); /* * Class: TestJNI * Method: get * Signature: ()I */ JNIEXPORT jint JNICALL Java_TestJNI_get (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。
jint是以JNI为中介使Java的int类型与本地的int沟通的一种类型。
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 |
对于数组型参数,
Java | C/C++ |
boolean[ ] | JbooleanArray |
byte[ ] | JbyteArray |
char[ ] | JcharArray |
short[ ] | JshortArray |
int[ ] | JintArray |
long[ ] | JlongArray |
float[ ] | JfloatArray |
double[ ] | JdoubleArray |
函数的名称是Java_+Java程序的package路径+函数名组成的。
Dynamic linkers resolve entries based on their names. A native method name is concatenated from the following components:
- the prefix Java_
- a mangled fully-qualified class name
- an underscore (“_”) separator
- a mangled method name
- for overloaded native methods, two underscores (“__”) followed by the mangled argument signature
对于本地方法参数来说
函数的名称是Java_+Java程序的package路径+函数名组成的。
第一个参数JNIEnv接口指针,指向一个个函数表,函数表中的每一个入口指向一个JNI函数。本地方法经常通过这些函数来访问JVM中的数据结构。下图演示了JNIEnv这个指针:
第二个参数根据本地方法是一个静态方法还是实例方法而有所不同。本地方法是一个静态方法时,第二个参数代表本地方法所在的类;本地方法是一个实例方法时,第二个参数代表本地方法所在的对象。如上例子当Java_TestJNI_get与Java_TestJNI_set是一个实例方法,因此jobject参数指向方法所在的对象。
继续编写对应的c语言的实现,如下:
#include <stdio.h> #include "TestJNI.h" int i=0; JNIEXPORT void JNICALL Java_TestJNI_set (JNIEnv * env, jobject obj, jint j) { i=j*888; } JNIEXPORT jint JNICALL Java_TestJNI_get (JNIEnv * env, jobject obj){ printf("ok!You have successfully passed the Java call c\n"); return i; }
gcc -Wall -fPIC -c TestJNI.c -I ./ -I /home/mazhi/workspace/jdk1.8.0_192/include/linux/ -I /home/mazhi/workspace/jdk1.8.0_192/include/ 命令生成TestJNI.o文件。
命令中-Wall:打开警告开关。-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
编译~/.bashrc文件,添加环境变量的配置export LD_LIBRARY_PATH=./ 使用source ~/.bashrc命令使配置生效
java TestJNI对文件进行运行即可。
参考文章:
https://jingyan.baidu.com/album/6c67b1d68e33bc2787bb1ee6.html?picindex=3