JNI编程:JAVA调用C++代码

一、简介

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中调用。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReWz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值