Jni快速入门
简单的例子
- 创建一个java项目,新建一个TestMain类,输入以下代码
package com;
public class TestMain {
//加载动态库,不用加扩展名,windows下会加载testJni.dll,linux下会加载testJni.so
static {
System.loadLibrary("testJni");
}
//声明一个本地接口
public static native void helloJni();
public static void main(String[] args) {
helloJni();
}
}
- 打开TestMain所在文件夹,在地址栏中输入cmd。按回车键打开命令提示符,输入 javac -h ./jni TestMain.java。会生成一个Jni文件夹,并在此文件夹下生成C++头文件。(如果使用javah命令,需要返回到包名的同级目录,例如本例中的包名为com,工程源码目录为src/com/TestMain.java。需要定位到src目录下,使用命令javah -d ./jni com.TestMain 否则会报错找不到类)
- 打开vs2019,创建c++ win32空项目testJni(也可以创建动态链接库项目)。将上一步生成的头文件复制到工程文件夹下,在vs中右键点击工程名——添加——现有项,将其添加到工程中。
- 此时#include<jni.h>这一行代码会报错,提示无法打开源文件。我们需要给工程配置一下java的环境,在工程名上点击右键——属性,打开工程配置对话框,注意平台根据所需选择,我这里使用x64。配置类型修改为动态库(.dll)
- 点击VC++目录——外部包含目录。将本机上的Jdk目录的include和include/win32添加到外部包含目录中。
- 在vs中右键点击工程名程——添加——新建项。创建一个CPP文件,命名为comTestMain。引入上面生成的头文件,并实现头文件中的函数。代码如下
#include"com_TestMain.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_TestMain_helloJni
(JNIEnv*, jclass) {
printf("helloJni");
}
- 在工程名上点击右键——生成,生成名为testJni.dll的动态库。将testJni.dll复制到java的工程目录中,运行java项目。如果名称、位置没有错,程序输出helloJni.
进阶
1. 添加参数
接下来增加两个接口来测试参数的传递
package com;
public class TestMain {
//加载动态库,不用加扩展名,windows下会加载testJni.dll,linux下会加载testJni.so
static {
System.loadLibrary("testJni");
}
//声明一个本地接口
public static native void helloJni();
//声明一个带基础类型参数与返回值的接口
public static native int sum(int a,int b);
//声明一个对象类型的带参数与返回值的接口
public static native Integer sum2(Integer a,Integer b);
public static void main(String[] args) {
helloJni();
System.out.println(" in Java sum(5,6) = " + sum(5,6));
int a = sum2(6,7);
System.out.println(" in Java sum2(6,7) = " + a);
}
}
再次执行javac -h ./jni TestMain.java。生成头文件,头文件中也相应增加了两个接口,可以看到对于基础类型int 对应的是jint。对于类类型,对应的是jobject。更多Jni数据类型介绍参考这里
JNIEXPORT jint JNICALL Java_com_TestMain_sum
(JNIEnv*, jclass, jint, jint);
JNIEXPORT jobject JNICALL Java_com_TestMain_sum2
(JNIEnv*, jclass, jobject, jobject);
在C++中实现这两个接口,以下代码展示了基础类型的使用,对象类型的值的获取,对像的创建。
#include"com_TestMain.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_TestMain_helloJni
(JNIEnv*, jclass) {
printf("helloJni");
}
JNIEXPORT jint JNICALL Java_com_TestMain_sum
(JNIEnv*, jclass, jint a, jint b) {
printf("\n c++: a+b = %d", a + b);
return a + b;
}
JNIEXPORT jobject JNICALL Java_com_TestMain_sum2
(JNIEnv* env, jclass jc, jobject a, jobject b) {
//获取对象的类
jclass per = env->GetObjectClass(b);
//获取方法ID,这个 intValue是 Integer类的一个方法。
//"()I"是指 intValue不带参数,返回值为Int,I是Jni中 int的 Java VM Type Signatures
jmethodID mid = env->GetMethodID(per, "intValue", "()I");
if (mid == NULL)
return b;
//调用intValue方法获取对像的值。
jint reslut = env->CallIntMethod(b, mid) + env->CallIntMethod(a, mid);
//另一种获取对像值的方法
jfieldID fida = env->GetFieldID(per, "value", "I");
jint value = env->GetIntField(a, fida);
//打印jobject a
printf("\n in c++ jobject a is %d", value);
//打印求和结果。
printf("\n in c++ Integer A + Integer B = %d", reslut);
//获取Integer类的value字段
jfieldID fid = env->GetFieldID(per, "value", "I");
//获取Integer类的构造函数,获取构造函数必须传<init>。第三个参数是构造函数的参数
jmethodID mid2 = env->GetMethodID(per, "<init>", "(I)V");
//创建一个新的Integer对象
jobject jo = env->NewObject(per ,mid2,reslut);
return jo;
}
生成动态库,复制到java工程目录下,运行java项目。输出如下。