阅读更多
1 JNI简介
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境
2 Demo详细步骤
以下操作基于macOS,不同的平台下,一些头文件的路径,以及生成动态库的方式不同,请注意区分
2.1 创建Java文件
1 | package org.liuyehcf.jni; |
- 加载本地Library,名字为"Hello",这是我们后面将会创建的动态链接库文件
- 调用native方法
2.2 利用命令行工具javah创建标准.h文件
javah用法
javah [options] <classes>- 其中,
[options]包括:-o <file>:输出文件 (只能使用 -d 或 -o 之一)-d <dir>: 输出目录-v -verbose:启用详细输出-h --help -?:输出此消息-version:输出版本信息-jni:生成 JNI 样式的标头文件 (默认值)-force:始终写入输出文件-classpath <path>:从中加载类的路径-cp <path>:从中加载类的路径-bootclasspath <path>:从中加载引导类的路径
<classes>是使用其全限定名称指定的
(例如, java.lang.Object)
命令如下(任选一种方式)
javah -classpath <.java或.class的路径都可以> -d <输出目录> org.liuyehcf.jni.JniDemojavah org.liuyehcf.jni.JniDemo:默认类加载路径是当前文件夹的路径,输出目录默认当前文件夹
生成的头文件org_liuyehcf_jni_JniDemo.h如下:
1 | /* DO NOT EDIT THIS FILE - it is machine generated */ |
比较重要的一点是#include <jni.h>这一句,编译器会从环境变量指定的路径中去查找jni.h,因此在编译时需要指定jni.h的路径,后面在生成动态库的时候会详细说明,这里先提一下
2.3 编写cpp文件
实现很简单,不废话,直接上代码
1 |
|
2.4 编译生成动态库文件
这一步有两种方式
第一种方式:利用-I参数指定依赖头文件的位置
g++ -dynamiclib -I <jni.h文件所在的目录> -I <jni_md.h文件所在的目录> <org_liuyehcf_jni_JniDemo.cpp的路径> -o <动态库的输出目录>/libHello.jnilib- 在我的电脑上,
jni.h文件所在目录如下:/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include
- 在我电脑上,
jni_md.h文件所在目录如下:/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include/darwin
第二种方式:修改org_liuyehcf_jni_JniDemo.h,并且将jni.h以及jni_md.h文件拷贝到org_liuyehcf_jni_JniDemo.h文件所在的目录中
- 修改
org_liuyehcf_jni_JniDemo.h文件,将第一句#include <jni.h>改成#include "jni.h" - 将
jni.h以及jni_md.h文件拷贝到org_liuyehcf_jni_JniDemo.h文件所在的目录中,这两个文件的目录参考上面的说明
g++ -dynamiclib <org_liuyehcf_jni_JniDemo.cpp的路径> -o <动态库的输出目录>/libHello.jnilib
g++参数解释
-dynamiclib:表示生成动态库,相当于linux环境下的-shared-I:指定编译的依赖的头文件所在的路径- 注意,如果
org_liuyehcf_jni_JniDemo.cpp就在当前目录下,那么不需要指定org_liuyehcf_jni_JniDemo.h的位置,否则需要
- 注意,如果
-o:指定生成的动态库文件的名称,在macOS环境下,动态库的命名格式为libXXX.jnilib,相当于linux下的libXXX.so
2.4.1 关于#include <filename>和#include “filename”
In practice, the difference is in the location where the preprocessor searches for the included file.
通常,这两者测差异在于预处理器去哪找这个file
For
#include "filename"the preprocessor searches in the same directory as the file containing the directive, and then like for#include <filename>. This method is normally used to include programmer-defined header files.
对于#include "filename"而言,预处理器在指令所在的目录中搜寻,后续处理与#include <filename>相同,这种方式通常用于包含自定义的头文件
For
#include <filename>the preprocessor searches in an implementation dependent manner, normally in search directories pre-designated by the compiler/IDE. This method is normally used to include standard library header files.
对于#include <filename>而言,预处理器以与实现相关的方式搜索,通常在编译器/ IDE预先指定的搜索目录中搜索,这种方式通常用于包含标准库头文件
3 运行
执行如下命令
java -classpath <org.liuyehcf.jni.JniDemo.class的加载路径> -Djava.library.path=<上面生成的libXXX.jnilib所在目录> org.liuyehcf.jni.JniDemo- 注意
-Djava.library.path参数指定的是libXXX.jnilib所在的目录
如果在IDE中运行,记得添加VM参数-Djava.library.path=<上面生成的libXXX.jnilib所在目录>