何为JNI
JNI是Java Native Interface的英文缩写, 中文翻译为本地调用, 自从Java 1.1开始就成为了Java标准的一部分.
C/C++是系统级的编程语言, 可以用来开发任何和系统相关的程序和类库, 但是Java本身编写底层的应用比较难实现, 使用JNI可以调用现有的本地库, 极大地灵活了Java的开发.
C/C++的效率是目前最好的语言, 可以使用C/C++来实现一些实时性非常高的部分. C/C++和Java本身都是非常流行的编程语言, 一些大型软件中经常使用语言之间的混合编程.
Java调用C/C++大概有以下4个步骤
- 编写带有native方法的Java类, 使用javac工具(在Eclipse中直接build项目)编译Java类
- 使用javah来生成与native方法对应的头文件
- 在Visual Studio中导入并实现java的头文件,编译为动态链接库(windows下是.dll, linux下是.so)
- 在Java代码中引用动态链接库
步骤1-生成.class文件
在Eclipse中新建项目JavaTest,新建包com.hisign.jni,新建类HelloJNI。代码如下
其中有一句比较重要, 这句话加载了动态类库。在windows下加载的就是.dll, 在linux下加载的就是.so。
System.loadLibrary(“HelloJNI2”);
package com.hisign.jni;
public class HelloJNI {
static{
System.loadLibrary("HelloJNI2");//加载生成的HelloJNI2.dll
}
public HelloJNI() {
}
public native void sayHello(String name);
}
在Eclipse中build项目,在bin目录下会生成HelloJNI.class文件。
步骤2-生成.h头文件
HelloJNI编写好之后,即可在DOS环境下将HelloJNI.java编译为HelloJNI.h头文件。进入 cmd 命令行,使用上一步生成的 .class 文件,利用 jdk 的 javah 命令生成 *.h 头文件。
javah命令
-classpath : 文件所在的根路径
-d : 输出 *.h 头文件的路径
-jni : 生成JNI样式的包头文件,可以理解成 *.class 文件的 包路径+类名此处比较诡异的是,-classpath 必须得是.class 的包路径文件的上一级;-jni 必须是.class 文件的 包路径+类名,否则会报错。
步骤3-实现头文件方法,生成动态链接库
创建一个C++项目
使C++项目包含jdk 目录下的 jni.h 、 jni_md.h 以及之前生成的 com_hisign_jni_HelloJNI.h。这里有两种方法一种是把上述三个文件复制到C++项目的include文件夹(需要新建该文件夹)。然后
在C++项目的“包含目录”添加上include文件夹。第二种方法不用复制上述三个头文件,可以直接在C++项目的“包含目录”添加上上述三个头文件原本所在的文件夹。我偷懒使用了第二种方法,如下图
实现头文件方法,新建类c++文件test.cpp,代码如下
#include <com_hisign_jni_HelloJNI.h>
#include <iostream>
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_hisign_jni_HelloJNI_sayHello
(JNIEnv* env, jobject obj, jstring name){
const char* pname = env->GetStringUTFChars(name, NULL);
std::cout << "Hello," << pname <<std::endl;
}
build项目在x64/Debug目录下生成HelloJNI2.dll文件。
C++项目中有两个坑,如果不熟悉VS的朋友容易掉坑里
第一坑是有关于你使用的操作系统位数,如果是32位那么默认是在32位平台编译,没有任何问题。如果是64位那么需要在配置管理器中把平台改为64位。(如果先做了上述添加头文件的操作再修改平台 需要在修改平台后再次添加头文件)
第二坑是若是64位平台默认的配置类型不是DLL,导致编译失败。需要在项目属性页做如下修改,再次build。
步骤4-在Java代码中引用动态链接库
在java项目下新建libs文件夹,把步骤3中生成的DLL文件复制进来。其实这个dll可以放在任何位置,但是需要配置Native library location, 配置位置在 工程属性->Java Build Path,配置Source标签或者Libraries标签下的Native library location都可以。
在java项目中添加测试类,查看是否成功调用DLL中实现的方法。