Jni快速入门

这里是Jni官网文档

简单的例子

  1. 创建一个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();
	}
}
  1. 打开TestMain所在文件夹,在地址栏中输入cmd。按回车键打开命令提示符,输入 javac -h ./jni TestMain.java。会生成一个Jni文件夹,并在此文件夹下生成C++头文件。(如果使用javah命令,需要返回到包名的同级目录,例如本例中的包名为com,工程源码目录为src/com/TestMain.java。需要定位到src目录下,使用命令javah -d ./jni com.TestMain 否则会报错找不到类)
    生成头文件
  2. 打开vs2019,创建c++ win32空项目testJni(也可以创建动态链接库项目)。将上一步生成的头文件复制到工程文件夹下,在vs中右键点击工程名——添加——现有项,将其添加到工程中。
    添加到工程
  3. 此时#include<jni.h>这一行代码会报错,提示无法打开源文件。我们需要给工程配置一下java的环境,在工程名上点击右键——属性,打开工程配置对话框,注意平台根据所需选择,我这里使用x64。配置类型修改为动态库(.dll)
    工程配置动态库
  4. 点击VC++目录——外部包含目录。将本机上的Jdk目录的include和include/win32添加到外部包含目录中。
    jdk配置
  5. 在vs中右键点击工程名程——添加——新建项。创建一个CPP文件,命名为comTestMain。引入上面生成的头文件,并实现头文件中的函数。代码如下
#include"com_TestMain.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_TestMain_helloJni
(JNIEnv*, jclass) {
	printf("helloJni");
}
  1. 在工程名上点击右键——生成,生成名为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项目。输出如下。
运行结果

posted on 2022-01-28 15:41  曹操是个好同志  阅读(498)  评论(0编辑  收藏  举报