使用jni进行java和c++相互操作

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++ 类型本地描述
booleanjbooleanC/C++8位整型
bytejbyteC/C++带符号的8位整型
charjcharC/C++无符号的16位整型
shortjshortC/C++带符号的16位整型
intjintC/C++带符号的32位整型
longjlongC/C++带符号的64位整型
floatjfloatC/C++32位浮点型
doublejdoubleC/C++64位浮点型

java方法签名获取

1.javap -c Order.class
2.idea里面使用asm插件

结束

本文为原创文章,转载请申请

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值