JNI编程:JAVA调用C++代码
一、简介JNI是Java Native Interface的缩写,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了
一、简介
JNI是Java Native Interface的缩写,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
二、正文
1.在JAVA中定义接口
如果想要在JAVA中调用C++代码,那么首先就要定义一个接口,然后我们再使用C++或者其他语言去实现这个接口。
定义这个接口十分简单,其实就一个关键字native
,如下我们就定义了一个接口。
2.用javah命令生成c++接口
javah命令是专门用来处理JNI使用Javah 可以生成 C/C++头文件,其中包含Java 代码中所有本地方法(native方法)的 JNI 存根
其命令的具体使用方式如下:
-d 和-o
这两个参数用于设置生成的C\C++头文件的指定,该两参数选项不能同时使用,-d是为中的每个有JNI方法的java类都生成一个头文件,并存放在-d指定的目录中,-o则是生成的所有JNI方法的头文件都放在-o指定的文件中。
-jin
表示用于生成JNI风格的C\C++头文件,默认该参数就是开启的。
-classpath
使用-classpath后JDK将不再使用CLASSPATH中的类搜索路径,如果-classpath和CLASSPATH都没有设置,则JDK使用当前路径(.)作为类搜索路径。
推荐使用-classpath来定义JDK要搜索的类路径,而不要使用环境变量CLASSPATH的搜索路径,以减少多个项目同时使用CLASSPATH时存在的潜在冲突。例如应用1要使用a1.0.jar中的类G,应用2要使用 a2.0.jar中的类G,a2.0.jar是a1.0.jar的升级包,当a1.0.jar,a2.0.jar都在CLASSPATH中,JDK搜索到第一个包中的类G时就停止搜索,如果应用1应用2的虚拟机都从CLASSPATH中搜索,就会有一个应用得不到正确版本的类G。
-verbose
该参数,将显示javah命令搜索和装置类文件的详细过程。
来源
现在来操作一下
第一步,先到源文件目录下使用javac
命令将源代码编译成class文件
第二步,进入src目录,使用javah生成c++的头文件
只需两步,即可生成C++的接口,无需我们自己定义。
下面看看这个文件定义了什么
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class regan_JNItest */
#ifndef _Included_regan_JNItest
#define _Included_regan_JNItest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: regan_JNItest
* Method: play
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_regan_JNItest_play
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
首先有一个宏JNIEXPORT
,这个宏定义在jni.h
文件中有定义,如下
#define JNIEXPORT __declspec(dllexport)
可以看到,实际上就是将导出DLL函数的宏换个名字而已
还有一个定义了一个JNICALL
宏,这个宏也定义在jni.h
文件中,如下
#define JNICALL __stdcall
可以看到,实际上就是将标准函数调用换个名字而已,因为JAVA的函数调用方式为stdcall
3.用C++实现在JAVA中定义的接口
直接用VS创建一个C++动态链接库项目,然后将刚刚使用javah命令生成的头文件复制到项目中。
因为生成的头文件会依赖jni.h
,而’jni.h’又会依赖’jni_md.h’,如下
所以我们需要将这两个头文件复制到项目中,并且把<>
改成""
这两个文件分别在
C:\Program Files\Java\jdk1.8.0_191\include
C:\Program Files\Java\jdk1.8.0_191\include\win32
将三个头文件都导入项目之中以后,我们就可以开始实现接口了。
在这里,我实现了一个播放音乐的功能,如下
#include "Dll.h"
#include <iostream>
#include <windows.h>
#pragma comment(lib,"winmm.lib")
using namespace std;
JNIEXPORT void JNICALL Java_test1_JNItest_play(JNIEnv *, jobject o){
cout << "正在播放音乐" << endl;
mciSendString("play F:\\音频资料\\天气预报.mp3", NULL, 0, NULL);
int a;
scanf("%d", &a);
}
注意,使用mciSendString
函数,需要导入winmm.lib
包
mciSendString
函数(实际上就是像MCI发送命令),可以在MSDN中查到,定义如下:
另外需要注意的是,如果你的JDK是64位的,那么你需要在VS中将你的平台设置为64位
OK,完成上述步骤之后,直接编译项目,进入项目目录/x64/Debug/
下,即有一个.dll文件,这就是我们实现的接口就在这个动态链接库中
4.在JAVA中加载动态链接库
直接使用System.load()
方法即可进行加载
public class JNItest {
{
System.load("F:\\All_C++_Pro\\JniDll\\x64\\Debug\\JniDll.dll");
}
public static void main(String[] args) {
new JNItest().play();
}
public native void play();
}
最终运行结果如下:
三、总结
对于JAVA不擅长的任务,以后可以使用C++来实现,比如可以在C++中用opencv实现一些函数,我们可以直接在JAVA中进行调用。一些调用硬件设备的功能,也可以在C++中实现,然后再JAVA中调用。
更多推荐
所有评论(0)