JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java 1.1 开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。
主要分为以下几个步骤
编写带有native声明的方法的java类
使用javac命令编译所编写的java类
使用javah -jni 类的全限定名
生成扩展名为h的头文件
使用C/C++实现本地方法
将C/C++编译的文件生成动态连接库(dll文件)
在java项目中引入生成的C/C++库文件
1.这里以HelloWorld为例,java代码如下:
package com.aliencat.javabase.jni;
public class HelloJni {
public native String sayHello(String name);
static{
System.loadLibrary("DLL1");
}
public static void main(String[] args) {
System.out.println(new HelloJni().sayHello("hello"));
}
}
声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为 native的,并且不能实现。
Load动态库:System.loadLibrary(“hello”);加载动态库(我们可以这样理解:我们的方法 displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“hello”是动态库的名字。
打开控制台,进入java目录下输入以下指令:
javah -jni com.aliencat.javabase.jni.HelloJni
即可生成一个一个头文件com_aliencat_javabase_jni_HelloJni.h
,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_aliencat_javabase_jni_HelloJni */
#ifndef _Included_com_aliencat_javabase_jni_HelloJni
#define _Included_com_aliencat_javabase_jni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_aliencat_javabase_jni_HelloJni
* Method: sayHello
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
(JNIEnv*, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
这个h文件相当于我们在java里面的接口,这里声明了一个Java_com_aliencat_javabase_jni_HelloJni_sayHello
(JNIEnv*, jobject, jstring);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。其中jobject为java的this引用,而jstring即是传入的字符串参数。
下载地址:https://visualstudio.microsoft.com/zh-hant/vs/whatsnew/
然后安装选择以下组件即可:
选择创建动态链接库
JDK目录的include目录下有一个jni.h的文件,include的win32目录下有个jni_md.h文件,还有java类生成C头文件HelloJni.h,也一起拷贝到DLL工程的目录下。主要在目录的头文件上右键选择添加->现有项:
hello.cpp即为我创建的c++文件。
本地方法和javah命令生成的头文件里面声明的方法名相同。
代码如下:
#include "pch.h"
#include "com_aliencat_javabase_jni_HelloJni.h"
#include <iostream>
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
(JNIEnv* env, jobject, jstring jstr) {
//将jstring类型转换成char*类型
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1); //new char[alen+1];
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
printf("%s ", rtn);
printf("this is C++ print");
const char* str = "this is come from C++ ";
return env->NewStringUTF(str);
}
首先选中项目根目录后右键点击属性,选择配置管理器,配置如下:
选中项目根目录后右键点击生成即可,控制台输出如下即表示成功:
以idea为例:
选择你的vs编译输出的文件夹(看你的vs控制台输出的位置)即可,我的是在以下面路径下:
?
?
加载本地库后即可使用
static{
System.loadLibrary("DLL1");
}
执行main方法后输出如下:
this is come from C++
hello this is C++ print
Java类型 | 本地类型(JNI) | 描述 |
---|---|---|
boolean(布尔型) | jboolean | 无符号8比特 |
byte(字节型) | jbyte | 有符号8比特 |
char(字符型) | jchar | 无符号16比特 |
short(短整型) | jshort | 有符号16比特 |
int(整型) | jint | 有符号32比特 |
long(长整型) | jlong | 有符号64比特 |
float(浮点型) | jfloat | 32比特 |
double(双精度浮点型) | jdouble | 64比特 |
void(空型) | void | N/A |