JNI调用C++方法指南

概述

JNI(Java Native Interface)用来在Java中调用其他语言编写的代码。本文主要介绍如何在Java中调用C++程序。

测试环境:Ubuntu 20.04.6 LTS, JDK 8, G++ 9.4.0

JNI的入门

Java中提供了native关键字,用于指示方法实现由本地代码来提供。对于C++程序而言,可以将源码打包为静态库动态库。在JNI中通常使用动态库,这样可以避免将Java字节码和本地代码混合在同一个的二进制文件中。

下面通过一个示例来演示JNI的具体用法。

步骤1:定义Java类HelloJNI,其源代码如下:

public class HelloJNI {

    static {
        System.loadLibrary("native"); // 加载指定的本地库, 使得可以通过JNI调用库中定义的本地方法
    }
    
    public static void main(String[] args) {
        new HelloJNI().sayHello("Linux");
    }

    private native void sayHello(String name); // 通过native修饰的本地方法, 需要将其链接到本地代码实现
}

步骤2:通过命令javac -h . HelloJNI.java编译Java源文件并自动生成C++头文件,生成的HelloJNI.h的源码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloJNI
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_HelloJNI_sayHello
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

本地方法声明JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject, jstring)中包含三个参数:

  • JNIENV *:代表Java环境,通过该指针可以对Java端的代码进行操作
  • jobject:代表该native方法对应的类对象实例。注意,若该native方法有static修饰,则其代表这个native方法对应的类实例
  • jstring:传递的参数,对应Java中的String类型

步骤3:定义头文件同名的文件HelloJNI.cpp,并在其中实现sayHello()方法:

#include "HelloJNI.h"
#include <iostream>

JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JJNIEnv* env, jobject thisObject, jstring name){
  const char* str = env->GetStringUTFChars(name, NULL);
  std::cout << "Hello " << str << std::endl;
}

步骤4:编译C++代码为动态库:

# 获取.o文件
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux HelloJNI.cpp
# 打包
g++ -shared HelloJNI.o -o libnative.so

步骤5:运行Java代码:

java -Djava.library.path=. HelloJNI

# 结果
Hello Linux

说明:-Djava.library.path=.:指定JVM查找本地库的路径为当前目录。

JNI的高级功能

JNI提供了一组特定的类型用于在C++和Java语言中传递数据。

基本数据类型

下表描述了Java基本类型在C++中对应的本地类型。

Java TypeNative TypeDescription
booleanjbooleanunsigned 8 bits
bytejbytesigned 8 bits
charjcharunsigned 16 bits
shortjshortsigned 16 bits
intjintsigned 32 bits
longjlongsigned 64 bits
floatjfloat32 bits
doublejdouble64 bits
voidvoidnot applicable

引用类型

JNI还支持多种应用类型,其对应关系如下:

Java TypeNative TypeDescription
ClassjclassClass类型
ObjectjobjectJava中的对象
Stringjstring字符串
Object[]jobjectArray对象类型数组
boolean[]jbyteArray布尔型数组
char[]jcharArray字符型数组
short[]jshortArray短整型数组
int[]jintArray整形数组
long[]jlongArray长整型数组
float[]jfloatArray浮点型数组
double[]jdoubleArray双浮点型数组

使用示例

Java类源码:

public class TypeTest {
    static{
        System.loadLibrary("typetest");
    }
    public static void main(String[] args) {
        TypeTest test = new TypeTest();

        int[] arr = {3, 1, 5, 9, 2};

        System.out.println(test.add(4, 5));
        System.out.println(test.circle(5.0));

        int[] brr = test.sortArray(arr);
        for(int num:brr){
            System.out.print(num + " ");
        }
        System.out.println();
    }
    
    public native int add(int a, int b);
    public native double circle(double radius);
    public native int[] sortArray(int[] array);
}

C++类头文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TypeTest */

#ifndef _Included_TypeTest
#define _Included_TypeTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TypeTest
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_TypeTest_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     TypeTest
 * Method:    circle
 * Signature: (D)D
 */
JNIEXPORT jdouble JNICALL Java_TypeTest_circle
  (JNIEnv *, jobject, jdouble);

/*
 * Class:     TypeTest
 * Method:    sortArray
 * Signature: ([I)[I
 */
JNIEXPORT jintArray JNICALL Java_TypeTest_sortArray
  (JNIEnv *, jobject, jintArray);

#ifdef __cplusplus
}
#endif
#endif

C++类方法实现:

#include "TypeTest.h"
#include <algorithm>

const double PI = 3.14;

JNIEXPORT jint JNICALL Java_TypeTest_add(JNIEnv * env, jobject obj, jint a, jint b){
    return a + b;
}

JNIEXPORT jdouble JNICALL Java_TypeTest_circle(JNIEnv * env, jobject obj, jdouble radius){
    return PI * radius * radius;
}

JNIEXPORT jintArray JNICALL Java_TypeTest_sortArray(JNIEnv * env, jobject obj, jintArray arr){
    // 获取数组长度
    jsize length = env->GetArrayLength(arr);
    // 获取数组元素
    jint* elements = env->GetIntArrayElements(arr, 0);
    std::sort(elements, elements + length);
    
    // 创建新的 jintArray 并设置排序后的元素
    jintArray result = env->NewIntArray(length);
    env->SetIntArrayRegion(result, 0, length, elements);

    // 释放原始数组
    env->ReleaseIntArrayElements(arr, elements, 0);

    return result;
}

编译运行参考JNI入门章节,不作重复赘述。

结语

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斯曦巍峨

码文不易,有条件的可以支持一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值