版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一、概述
JNI 全称 java native interface。
使用环境:当 java api 不能满足我们程序的需要的时候,比如算法计算,图像渲染中,效率要求非常高,又或者需要访问一些已有的本地库。
二、JNI 步骤
1.编写 native 方法
在 java 代码中申明 native 的方法。
public class JNIMain {
public native static String getString();
}
2.生成 .h 文件
在命令行窗口中切换到项目的 src 目录下,使用 javah 命令生成对应的 .h 文件。
注:必须切到 src 这个目录,javah 后面跟的是包名与类名。
刷新项目:
生产的 .h 文件中的方法名 是 java_类的全名_方法名 ,也可以手动进行生成。
3.复制 .h 头文件到 cpp 工程
把上方生成的 com_xiaoyue_JNIMain.h 文件复制到 C 项目工程所在文件目录中(这边采用的是 VS 创建 C 项目)。
在 VS 的项目结构中,添加 com_xiaoyue_JNIMain.h 文件。
这时候 com_xiaoyue_JNIMain.h 会报一些错误,需要进行修改。
4.复制 jni.h 和 jni_md.h
com_xiaoyue_JNIMain.h 需要用到 java 的两个 .h 文件,这两个头文件在 JDK 里面就有。
jni.h:
jni_md.h:
把这两个头文件按第三步复制到 C 项目工程下,同时进行导入。
5.修改 com_xiaoyue_JNIMain.h
修改 com_xiaoyue_JNIMain.h 文件下的 #include <jni.h>
为 #include "jni.h"
。
6.实现 .h 头文件中的声明函数
新建一个 .c 文件,实现 .h 文件的申明。
#include "stdafx.h"
#include "com_xiaoyue_JNIMain.h"
JNIEXPORT jstring JNICALL Java_com_xiaoyue_JNIMain_getString
(JNIEnv *env, jclass jciz) {
return (*env)->NewStringUTF(env, "Hello JNI");
}
7.生成一个dll 动态库
右击项目,选择属性。在属性窗口里设置项目类型为动态库。
设置预编译头不使用预编译头。(否则会报错)
右键生成 –> 重新生成解决方案。
这时候会生成 .dll 动态库,dll 是 window 环境下的动态库, so 是 linux 环境下的动态库。
8.在java中加载动态库
把生成的动态库拷贝到 java 项目工程里面。
在 java 代码中进行加载改动态库。
public class JNIMain {
public native static String getString();
static{
System.loadLibrary("FirstApplication");
}
}
9.触发 native 函数
JNI 的调用跟 java 普通方法的调用没有区别。
public class JNIMain {
public native static String getString();
static{
System.loadLibrary("FirstApplication");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(getString());
}
}
结果:
三、分析
1.动态库和静态库
都是函数库
静态库:在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。
动态库:在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
2.JNIEnv
生成的 .h 文件中,方法有两个参数,第一个参数就是 JNIEnv。
右击选择跳转到定义处,可以发现这是定义在 jni.h 文件中(即我们从 jdk 下拷贝出来的)。
在 C 的情况下:JNIEnv 是一个指向 JNINativeInterface_ 的指针,而 JNINativeInterface_ 是一个结构体,里面包含众多的函数指针。可通过这些方法与 java 进行交互。所以通过 JNIEnv 可以调用到 java 的方法。
在 C ++ 的情况下, JNIEnv 就是一个 JNIEnv_ 变量,在 JNIEnv_ 里面包含一个 JNINativeInterface_ 的指针。
四、静态与非静态方法
上面写的方法是一个静态的,这边在添加一个非静态的方法。
java 代码:
public class JNIMain {
public native static String getString();
public native String getString2();
static{
System.loadLibrary("FirstApplication");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(getString());
JNIMain jniMain = new JNIMain();
jniMain.getString2();
}
}
.h 文件:
/*
* Class: com_xiaoyue_JNIMain
* Method: getString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_xiaoyue_JNIMain_getString
(JNIEnv *, jclass);
/*
* Class: com_xiaoyue_JNIMain
* Method: getString2
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_xiaoyue_JNIMain_getString2
(JNIEnv *, jobject);
主要区别是生成的 .h 文件第二个参数,静态的是 jclass, 非静态的是 jobject。
jclass 表示调用静态方法的类,在这边即 JNIMain。
jobject 表示调用非静态方法的对象,这边指创建出来的 JNIMain 的实例 jniMain。