1.介绍
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 [1] 从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
2.编写java代码
public class JniMain {
static {
//System.loadLibrary("jni_test");
}
public static void main(String[] args) {
String realPath = JniMain.class.getClassLoader().getResource("")
.getFile()+"jni_test";
if(System.getProperty("os.name").toLowerCase().startsWith("win")){
System.load(realPath+".dll");
}else{
System.load(realPath+".so");
}
System.out.println("realPath = " + realPath);
MathInterface mathInterface = new MathInterface();
int result = MathInterface.add(8, 9);
System.out.println("result = " + result);
Integer integer = mathInterface.addInteger(20, 18);
System.out.println("integer = " + integer);
Order order = mathInterface.getOrder(18L);
System.out.println("order = " + order);
}
}
package com.git;
import lombok.Data;
/**
* @author authorZhao
* @since 2021-03-10
*/
@Data
public class Order {
private Long orderNo;
private String name;
}
package com.git;
public class MathInterface {
public native static int add(int i,int j);
public native Integer addInteger(Integer i,Integer j);
public native Order getOrder(Long orderNo);
public boolean checkOrder(Order order){
System.out.println("java开始订单校验");
Long orderNo = order.getOrderNo();
System.out.println("orderNo = " + orderNo);
boolean b = orderNo % 2 == 0;
System.out.println("java结束订单校验");
return b;
}
}
3.编译java代码,生成jni头文件
// 本文用的idea,直接去class文件下面使用命令行
javah -classpath . -jni com.git.MathInterface
新版本使用 javac -h 新版本大概是>=17
4.创建c/c++项目,本文使用qt,linux下面推荐clion
qtcreator创建lib项目,linux下面推荐直接编译为动态库
CONFIG -= qt
TEMPLATE = lib
DEFINES += JNI_TEST_LIBRARY
CONFIG += c++11
CONFIG += dll
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
com_git_MathInterface.cpp
HEADERS += \
com_git_MathInterface.h \
jawt_md.h \
jni.h \
jni_md.h
# Default rules for deployment.
unix {
target.path = /usr/lib
}
!isEmpty(target.path): INSTALLS += target
头文件和实现
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <iostream>
/* Header for class com_git_MathInterface */
#ifndef _Included_com_git_MathInterface
#define _Included_com_git_MathInterface
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_git_MathInterface
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_git_MathInterface_add
(JNIEnv *, jclass, jint a, jint b);
/*
* Class: com_git_MathInterface
* Method: addInteger
* Signature: (Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;
*/
JNIEXPORT jobject JNICALL Java_com_git_MathInterface_addInteger
(JNIEnv *, jobject, jobject a, jobject b);
/*
* Class: com_git_MathInterface
* Method: getOrder
* Signature: (Ljava/lang/Long;)Lcom/git/Order;
*/
JNIEXPORT jobject JNICALL Java_com_git_MathInterface_getOrder
(JNIEnv * jni, jobject object, jobject orderNo);
#ifdef __cplusplus
}
#endif
#endif
#include "com_git_MathInterface.h"
using namespace std;
JNIEXPORT jint JNICALL Java_com_git_MathInterface_add
(JNIEnv * jni, jclass clazz, jint a, jint b){
//类型对应的上直接写
return a+b;
}
JNIEXPORT jobject JNICALL Java_com_git_MathInterface_addInteger
(JNIEnv * jni, jobject obj, jobject a, jobject b){
//参数和返回值都是Integer类型,属于包装类型,对应c++为jobject
jclass integerClass =jni->GetObjectClass(a);
//使用Integer的value获取jint类型
jfieldID id_age = (jni)->GetFieldID(integerClass, "value", "I");
jint ac = jni->GetIntField(a,id_age);
jfieldID bId = (jni)->GetFieldID(integerClass, "value", "I");
jint bc = jni->GetIntField(b,bId);
jint result = ac+bc;
//获取Integer的静态放法
jmethodID valueOf = jni->GetStaticMethodID(integerClass,"valueOf","(I)Ljava/lang/Integer;");
//使用Integer.valueOf方发创建对象
jobject c = jni->CallStaticObjectMethod(integerClass,valueOf,result);
//返回Integer类型
return c;
}
//这里说一下,第一个object相当于this,如果是一个静态方法,就是该类的类对象
JNIEXPORT jobject JNICALL Java_com_git_MathInterface_getOrder
(JNIEnv * jni, jobject object, jobject orderNo){
jclass longClass =jni->GetObjectClass(orderNo);
//获取Order的类对象
jclass orederClass =jni->FindClass("Lcom/git/Order;");
//jmethodID valueOf = jni->GetStaticMethodID(longClass,"valueOf","(J)Ljava/lang/Long;");
//jobject orderNo = jni->CallStaticObjectMethod(longClass,valueOf,orderNo);
cout<<"c++创建订单对象"<<endl;
jmethodID newOrder = jni->GetMethodID(orederClass,"<init>","()V");
cout<<"c++设置订单号"<<endl;
jobject order = jni->NewObjectA(orederClass, newOrder,0);
jmethodID setOrderNo = jni->GetMethodID(orederClass,"setOrderNo","(Ljava/lang/Long;)V");
jni->CallObjectMethod(order,setOrderNo,orderNo);
cout<<"c++设置订单名称"<<endl;
jstring orderName = jni->NewStringUTF("订单008");
jmethodID setName = jni->GetMethodID(orederClass,"setName","(Ljava/lang/String;)V");
jni->CallObjectMethod(order,setName,orderName);
//调用校验订单的方法
jclass thisClass =jni->GetObjectClass(object);
jmethodID checkOrder = jni->GetMethodID(thisClass,"checkOrder","(Lcom/git/Order;)Z");
cout<<"c++开始执行校验订单"<<endl;
jboolean checkResult = jni->CallBooleanMethod(object,checkOrder,order);
cout<<"c++订单校验完毕"<<endl;
if(checkResult == JNI_FALSE){
cout<<"订单校验失败"<<endl;
return nullptr;
}
return order;
}
5.测试结果
6.注意事项
动态库的生成:
- qt直接构建就会在debug目录生成dll文件(win下面)
- linux下面直接用g++,反正就几个文件 gcc -c -fPIC **.c **.c gcc -shared -o jni_test.so *.o
- 动态库的位置,如果使用System.loadLibrary(“jni_test”);方法可以放在jnk的bin目录下面或者系统默认库下面
- 本文采用System.load(realPath+“.dll”);目的是为了加载jar包下面的,该方法需要文件绝对路径和后缀,非spring环境如上,spring环境使用resource更简单
- 相互调用的困难在于类型的转化和c++段内存的管理
7.参考资料
jni基本数据类型对应关系
Java类型 | c/c++ 类型 | 本地描述 |
---|---|---|
boolean | jboolean | C/C++8位整型 |
byte | jbyte | C/C++带符号的8位整型 |
char | jchar | C/C++无符号的16位整型 |
short | jshort | C/C++带符号的16位整型 |
int | jint | C/C++带符号的32位整型 |
long | jlong | C/C++带符号的64位整型 |
float | jfloat | C/C++32位浮点型 |
double | jdouble | C/C++64位浮点型 |
java方法签名获取
1.javap -c Order.class
2.idea里面使用asm插件
结束
本文为原创文章,转载请申请