这里的内容是jni编程的最基础介绍,例如生成头文件,链接。了解这是能更好的了解c和java之间的关系。而关于语法编程方面的内容,可以查看网上的帖子,例如:
https://www.jianshu.com/p/aba734d5b5cd
http://blog.csdn.net/freechao/article/details/7692239
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
JNI 包括相互调用的两部分:
允许你把一个JVM嵌入到本地程序中。
本地程序可以链接一个实现了JVM的本地库,然后使用“调用接口”执行JAVA语言编写的软件模块。
JNI的副作用
一旦使用JNI,JAVA程序就丧失了JAVA平台的两个优点:
1、程序不再跨平台
。要想跨平台,必须在不同的系统环境下重新编译本地语言部分。
2、程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。一个通用规则是,你应该让本地方法集中在少数几个类当中。这样就降低了JAVA和C之间的耦合性。
实现步骤
1,使用 native 修饰一个想要在调用本地方法的抽象方法。
public class HelloJNI {
public static void main(String[] args) {
new HelloJNI().displayHelloWorld();
}
// 载入本地库
static {
System.loadLibrary("nativeLib");
}
// 所有使用native关键字修饰的都是对本地方法的声明。
public native int displayHelloWorld();
}
声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明该方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。 Load动态库:System.loadLibrary("hello");加载动态库(我们可以这样理解:我们的方法 displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“hello”是动态库的名字。
2,编译
没有什么好说的了
javac HelloJNI.java -d .
3,生成扩展名为h的c/c++头文件
使用指令 javac -h <生成的头文件放置目录> <源文件>
如:javac -h . HelloJNI.java
生成如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class hellojni_HelloJNI */
#ifndef _Included_hellojni_HelloJNI
#define _Included_hellojni_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: hellojni_HelloJNI
* Method: displayHelloWorld
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_hellojni_HelloJNI_displayHelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
(这里我们可以这样理解:这个h文件相当于我们在java里面的接口,这里声明了一个Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致)。
在上面的代码中看到了JNIEXPORT和JNICALL关键字,这两个关键字是两个宏定义,他主要的作用就是说明该函数为JNI函数,在Java虚拟机加载的时候会链接对应的native方法,JNIEXPORT 表示输出类型;JNICALL表示参数的压栈顺序;貌似都可以省略.
4,实现由javah命令生成的头文件里面声明的方法。
jni.h是定义了很多我们使用的JNI函数和结构体,jint是JNI定义的数据类型,因为Java层和C/C++的数据类型或者对象不能直接相互的引用或者使用,JNI层定义了自己的数据类型,用于衔接Java层和JNI层,至于这些数据类型我们在后面介绍。这里的jint对应Java的int数据类型,该函数返回的int表示当前使用的JNI的版本,其实类似于Android系统的API版本一样,不同的JNI版本中定义的一些不同的JNI函数。该函数会有两个参数,其中*jvm为Java虚拟机实例,JavaVM结构体定义了以下函数:
so动态链接库,so文件是linux下的c/c++动态链接库。Java虚拟机会在运行时动态加载so库,并运行。
##gcc -I /<HelloWorld.c b.c c.c .... > -fPIC -shared -o libtest.so
但是需要指定jni库和jni_mk库,一般在PATH里并没有配置。需要根据平台单独指定
gcc -I /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/include -I /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/include/darwin/ HelloWorld.c -fPIC -shared -o libtest.so
JAVA_HOME:
/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home
If you don't know where jni_md.h is, use find
:
find / -name jni_md.h 2>/dev/null