文章内容如题,只是对JNI使用的简单介绍。相信有许多同行也跟我一样在这方面知之甚少,所有在这里也总结一下,希望方便大家学习。
1.简介
JNI是Java Native Interface的缩写,它的设计目的是:
The standard Java class library may not support the platform-dependent features needed by your application.
You may already have a library or application written in another programming language and you wish to make it accessible to Java applications.
You may want to implement a small portion of time-critical code in a lower-level programming language, such as assembly, and then have your Java application call these functions
2.JNI的书写步骤
1)编写带有native声明方法的java类。
2)使用javac命令编译所编写的java类。
3)使用编译后的.class文件,运行javah -jni 类名 命令生成扩展名为.h的头文件。
4)使用C/C++实现本地方法。
也就是自定义native方法所调用的其他语言的文件,该文件中实现java中声明的native方法(本文以c语言为例,所以建立文件为.c扩展名)。
5)使用C/C++编写的文件(上述过程生成的.c文件)生成动态链接库文件。
即使用cl.exe命令
以上诸步骤我的理解总结如下:在java文件中如果想调用c的方法,需要通过声明的native方法来调用,调用什么方法呢,按照面向接口编程的思想需要事先定义一个供二者通信的接口吧,于是乎.h文件就出现了,java端通过javah命令生成这个接口文件,文件中声明了那些native方法,但是仅仅是定义一些基本信息(毕竟是接口嘛)比如:方法名、返回值、参数等。接口有了,想使用的话就需要对他进行实现了,这时就需要使用native语言(本例为C)对接口中方法进行实现了,这时就出现了步骤4中的.c扩展名的文件。文件有了还不行,需要对其进行转换,生成一个.dll文件,才能被java所使用,到了这一步大功告成。
下面就通过一个简单的helloworld实例演示上面的步骤:
1)编写java程序:
public class HelloWorld { //Load动态库:System.loadLibrary("hello");加载动态库(我们可以这样理解:我们的方法displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“hello”是动态库的名字。 static { System.loadLibrary("hello"); } //声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。 public native void displayHelloWorld(); //测试 public static void main(String[] args){ new HelloWorld().displayHelloWorld(); } }
2)编译成.class文件
javac HelloWorld.java
3)生成扩展名为.h的头文件
javah -jni HelloWorld
生成的头文件的内容:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class HelloWorld */ #ifndef _Included_HelloWorld #define _Included_HelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: HelloWorld * Method: displayHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
这里我们可以这样理解:这个h文件相当于我们在java里面的接口,这里声明了一个Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。如果需要在包深层次的某个class文件进行.h头文件的生成。则需要进行如下步骤:
·通过命令行进入到工程bin文件夹中
·输入命令:javah -classpath . packagepath.类名
以上命令将产生一个名为:packagepath_类名.h 的头文件(其中packagepath路径中.被替换成_出现在文件名中)
需要注意:方法名中一定要正确指向其实现的方法所在的.h文件,不然虽然能正确进行下面步骤,但是运行时却出现异常。例如:生成的.h文件名为:com_mazhj_HelloWorld.h,则.c文件中相应的实现方法名应该为:Java_com_mazhj_HelloWorld_displayHelloWorld(JNIEnv *, jobject)
4)编写本地方法
实现和由javah命令生成的头文件里面声明的方法名相同的方法。
#include "jni.h" #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj) { printf("Hello world!\n"); return; }
注意代码中的第1行,需要将jni.h文件引入,(可以在%JAVA_HOME%/include文件夹中找到)因为在程序中的JNIEnv、jobject等类型都是在该头文件中定义的;另外在第2行需要将HelloWorld.h头文件引入(以便将HelloWorld.h头文件里面声明的方法加以实现)。最后将上面代码保存为HelloWorldImpl.c文件。
5)生成动态库文件
以window平台为例,需要生成dll文件。在保存的HelloWorldImpl.c文件夹下面,使用VC的编译器cl.exe工具生成。
cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD HelloWorldImp.c -Fehello.dll
下面对该命令进行一下简单的介绍:
·CL.exe 是控制 Microsoft C 和 C++ 编译器与链接器的 32 位工具。编译器产生通用对象文件格式 (COFF) 对象 (.obj) 文件。链接器产生可执行文件 (.exe) 或动态链接库文件 (DLL)。
·参数-I:在目录中搜索包含文件。
参数-LD:创建动态链接库。
参数-Fe:重命名可执行文件
注意:生成的dll文件名在选项-Fe后面进行配置,这里是hello,因为在HelloWorld.java文件中我们loadLibary的时候使用的名字是hello。当然这里修改之后那里也需要修改。另外需要将-I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32参数加上,是因为在第4步里面编写本地方法的时候引入了jni.h文件。
6)运行程序
java HelloWorld
注意:生成的.dll文件需要放在系统path中,这样,Java进程在运行中才能找到本地库并动态加载。我们可以通过环境变量System.getProperty("java.library.path")来查看当前JVM搜索本地库的路径,这时,就会遇到一个问题,部署应用的时候要记住将本地库拷贝到环境变量path指定的路径中。一般在windows平台上直接copy到C:\WINDOWS\System32目录下了事(此方法好使)。但要换一台机器部署怎么办?除了要把Java程序拿过去,还要记的把本地库也copy到正确的目录,真麻烦。于是想看看有什么好办法来解决这个问题。首先,最容易想到的是,把本地库和class文件放在一起,利用Class.getResource(str)找到路径,然后加到环境java.library.path中:
URL url = Foo.class.getResource("Foo.class"); String path = (new File(url.getPath())).getParent(); System.setProperty("java.library.path", path);
看上去很好,但却不能工作。查了一下ClassLoader的源代码,原来它把搜索路径定义为静态变量并只初始化一次,后面再设置java.library.path就没有用了。 ClassLoader代码片断:
// The paths searched for libraries static private String usr_paths[]; static private String sys_paths[]; ... if (sys_paths == null) { usr_paths = initializePath("java.library.path"); sys_paths = initializePath("sun.boot.library.path"); }
正在一筹莫展是,翻看JACOB的源代码,忽然有了惊喜的发现。
try { //Finds a stream to the dll. Change path/class if necessary InputStream inputStream = getClass().getResource("/jacob.dll").openStream(); //Change name if necessary File temporaryDll = File.createTempFile("jacob", ".dll"); FileOutputStream outputStream = new FileOutputStream(temporaryDll); byte[] array = new byte[8192]; for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) { outputStream.write(array, 0, i); } outputStream.close(); temporaryDll.deleteOnExit(); System.load(temporaryDll.getPath()); return true; } catch(Throwable e) { e.printStackTrace(); return false; }
高,真是好办法。把dll放在classpath中,用Class.getResource(str).openStream()读取这个dll,然后写到temp目录中,最后用System.load(path)来动态加载。多说一句,为什么得到了jacob.dll的URL不直接去加载呢?想想看,如果把dll和class一起打成Jar包,ClassLoader还是不能加载本地库,因为System.load(path)需要的是dll的完整路径,但并不支持jar协议。还不明白,看看下面的代码:
URL url = Foo.class.getResource("/java/lang/String.class"); System.out.println(url.toExternalForm()); System.out.println(url.getFile());
运行结果:
jar:file:/C:/Program%20Files/Java/jdk1.6.0_21/jre/lib/rt.jar!/java/lang/String.class file:/C:/Program%20Files/Java/jdk1.6.0_21/jre/lib/rt.jar!/java/lang/String.class
ClassLoader中用new File(name),当然会找不到文件。同时,看看我的第一种方法,就算能设置成功环境java.library.path,如果dll是在jar包中,还是加载不了。
附:VC编译器下载地址:http://www.oamo.com/Software/Catalog130/1569.html
发表评论
-
jacob莫名奇妙抛异常
2010-11-01 18:17 930最近使用jacob写word文件的时候发现个奇怪的现象:写入数 ... -
正则表达式匹配换行
2010-10-19 19:19 3138因为早期使用正则表达式的工具是基于行的。它们都是一行一行的读入 ... -
volatile关键字一
2010-10-04 10:39 778转自IBM论坛文章,原题目:java理论与实践:正确使用vol ... -
volatile关键字二
2010-10-04 10:35 849java中int等不大于32位的类型上的简单操作都是原子操作, ... -
终结函数守卫者
2010-09-26 21:58 16581.引入:之所以要使用 ... -
System.getProperty()
2010-09-18 14:45 915java.version Java 运行时环境版本 java. ... -
重载(overload)与覆盖(override)
2010-08-01 13:03 763“重载(overload)”: 1、overload时,方法 ... -
用循环代替递归
2010-07-31 23:18 3167public class nhn { public s ...
相关推荐
私有原生String jniHelloWorld(); 使用javah生成C ++头 cd projectPath / app / src / main javah -d jni -cp pathTo / Library / Android / sdk / platforms / android-22 / android.jar:../../ build / ...
在线教学质量评价系统可以方便和全面地收集教师教学工作的数据,提供师生网上评教的评分结果,快速集中收集各方面的评教信息,使教务管理部门能够及时了解教学动态和师资情况,为教务老师提供相关决策支持,为职称评聘提供教学工作质量的科学依据,同时减轻了教务老师的工作量。
python-3.10.7-amd64.zip
自研扩散模型高光谱修复网络 基于MST_Plus_Plus 网络改造。 试验数据 扩散模型loss初步测试降到了0.005,比不加扩散loss小了20倍, 训练入口 train_cos_img.py
企业数据治理之数据安全治理方案
这是历年的毕业设计的项目,基于Android的一个红外防盗报警。需要自己添加蜂鸣器和热释电的硬件访问服务。
短视频用户价值研究报告2022
基于springboot的java毕业&课程设计
可运行源码(含数据库脚本)+开发文档+lw(高分毕设项目) java期末大作业毕业设计项目管理系统计算机软件工程大数据专业 内容概要:首先在日常的出行中,老旧城区道路狭窄,容易造成车辆的堵塞,每天早晚,接送孩子的车辆数密集,会造成相应的交通堵塞情况。而同样的,在停车的管理上,一方面我国的停车场面积较少,停车位一位难求,特别是在现在的一些小区里,为了抢停车位而产生的矛盾也日益突出。另一方面在停车场的管理上也存在着较大的管理问题,进车容易出车难是当下的停车场所出现的主要问题。而现在的停车场管理系统眼花缭乱,效果水平也良莠不齐,停车场的管理是当下各大城市的公共设施发展的一大难题,而国家、各大省市也都开 全套项目源码+详尽文档,一站式解决您的学习与项目需求。 适用人群: 计算机、通信、人工智能、自动化等专业的学生、老师及从业者。 使用场景及目标: 无论是毕设、期末大作业还是课程设计,一键下载,轻松部署,助您轻松完成项目。 项目代码经过调试测试,确保直接运行,节省您的时间和精力。 其他说明: 项目整体具有较高的学习借鉴价值,基础能力强的可以在此基础上修改调整,以实现不同的功能。
基于springboot的java毕业&课程设计
微信小程序设计之相关行业源码及图文导入教程
网络游戏开发是一项很大的工程,需要很多综合性的知识。这对于刚刚入门的开发者来说很难理解。本论文从研究开发一个模仿泡泡堂网络游戏的例子出发,讲述网络游戏开发中用到的一些最基本的知识和设计思想,使大家清晰的理解游戏开发的过程。 整个设计中利用java中的swing编程,结合游戏的操作流程,对整个游戏进行精心的设计和大量的测试,实现游戏软件服务器端和客户端的开发,为玩家提供一个友好美观的操作界面,并添加聊天等功能以增加玩家之间的互动性,此外实现了可编辑场景地图的功能,使得游戏内容的更加丰富,玩家交互性更好,确保了游戏更具有趣味性、灵活性,以满足玩家对这款网络游戏的要求。
外东洪路中段.m4a
软考3333333333
附件是Elasticsearch 的全文搜索功能使用方法,文件绿色安全,请大家放心下载,仅供交流学习使用,无任何商业目的!
1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
基于springboot的java毕业&课程设计
CCNP TSHOOT 642-832 Official Certification Guide
MySQL8.4.0 LTS(mysql-server_8.4.0-1ubuntu22.04_amd64.deb-bundle.tar)适用于Ubuntu 22.04 Linux (x86, 64-bit)