JNI数据类型及Java使用jni调用c++接口

一、JNI基本数据类型

1. 基本数据类型

JNI 提供了与 Java 基本类型对应的本地类型:

Java 类型JNI 类型C/C++ 类型大小/格式
booleanjbooleanunsigned char8 bits
bytejbytesigned char8 bits
charjcharunsigned short16 bits
shortjshortshort16 bits
intjintint32 bits
longjlonglong long64 bits
floatjfloatfloat32 bits
doublejdoubledouble64 bits

2. 引用数据类型

JNI 还定义了一组对应于 Java 对象的引用类型:

JNI 类型Java 类型
jobjectjava.lang.Object
jclassjava.lang.Class
jstringjava.lang.String
jarray数组
jobjectArrayObject[]
jbooleanArrayboolean[]
jbyteArraybyte[]
jcharArraychar[]
jshortArrayshort[]
jintArrayint[]
jlongArraylong[]
jfloatArrayfloat[]
jdoubleArraydouble[]
jthrowablejava.lang.Throwable

3. 类型签名

JNI 使用特定的签名来表示 Java 类型:

Java 类型类型签名
booleanZ
byteB
charC
shortS
intI
longJ
floatF
doubleD
类类型 (如 Ljava/lang/String;)L全限定类名;
数组类型 (如 [I 表示 int[])[类型
方法签名 (如 (II)I 表示 int func(int, int))(参数类型)返回类型

二、创建C++动态库

1. 创建一个c++项目

在这里插入图片描述

2. 添加JNI头文件

选中c++项目点击鼠标右键 > 属性
在这里插入图片描述
在这里插入图片描述

3. 创建cpp资源文件

#include "jni.h"

extern "C" 
{
	//obj参数为Java的Test类对象
	//Java_org_jni_Test_test Java为固定语法,org_jni为包的路径,Test为Java的类名,test为对应的Java方法名
	JNIEXPORT jint JNICALL Java_org_jni_Test_test(JNIEnv* env, jobject obj)
	{
		return 100;
	}
}

4. 生成动态库

1.选中C++项目鼠标右键
2.点击《生成》
这里选择生成对应的动态库
在这里插入图片描述

在C++项目根目录下有Debug或Release文件夹,生成的动态库就在这个文件夹中。windows只能生成dll动态库,so动态库需要在linux系统运行生成。

在这里插入图片描述

三、Java使用JNI调用C++接口

1. 声明C++接口

package org.jni;

public class Test {
    public native int test();
}

2. 加载动态库

static {
    //jni方式
    System.load("动态库的路径");
}

3. 测试C++接口

public static void main(String[] args) {
    Test t= new Test();
    System.out.println("t= " + t.test());
}

测试结果
在这里插入图片描述

四、JNI签名的使用方法

1. JNI方法的重载

C++代码:

#include <jni.h>

extern "C"
{
	//无参数
	JNIEXPORT jlong JNICALL Java_org_jni_Test_createPoint__(JNIEnv* env, jobject obj)
	{
		return 100;
	}
	//传递对象数组参数
	JNIEXPORT jlong JNICALL Java_org_jni_Test_createPoint___3Ljava_lang_Object_2(JNIEnv* env, jobject obj, jobjectArray array)
	{
		return 100;
	}
	//传递基本类型数组参数
	JNIEXPORT jlong JNICALL Java_org_jni_Test_createPoint___3J(JNIEnv* env, jobject obj, jlongArray ids)
	{
		return 100;
	}
	//传递集合
	JNIEXPORT void JNICALL Java_org_jni_Test_createPoint__Ljava_util_ArrayList_2
	(JNIEnv* env, jobject obj, jobject arr)
	{
	}
	//传递对象与集合
	JNIEXPORT void JNICALL Java_org_jni_Test_createPoint__Ljava_lang_Object_2Ljava_util_ArrayList_2
	(JNIEnv* env, jobject obj, jobject type, jobject arr)
	{
	}
	JNIEXPORT void JNICALL Java_org_jni_Test_createPoint__I(JNIEnv* env, jobject obj, jint num)
	{
	}
	
	JNIEXPORT void JNICALL Java_org_jni_Test_createPoint__Ljava_lang_String_2(JNIEnv* env, jobject obj, jstring text)
	{
	}
	
	JNIEXPORT void JNICALL Java_org_jni_Test_createPoint___3ID
	(JNIEnv* env, jobject obj, jintArray array, jdouble factor)
	{
	}
	
	JNIEXPORT void JNICALL Java_org_jni_Test_createPoint__I_3D
	(JNIEnv* env, jobject obj, jint index, jdoubleArray ds)
	{
	}
}

Java代码:

package org.jni;

import java.util.List;

public class Test {
	//无参数
    public native long createPoint();
    //传递对象数组参数
    public native long createPoint(Object[] objectArray);
    //传递基本类型数组参数
    public native long createPoint(long[] ids);
    //传递集合
    public native void createPoint(List<String> arr);
    //传递对象与集合
    public native void createPoint(Object type, List<String> arr);
    
    public native void createPoint(int num);
    public native void createPoint(String text);
    public native void createPoint(int[] array, double factor);
    public native void createPoint(int index, double[] ds);
}

2. JNI创建Java对象

核心代码:

	//javaClass 为Java 类的全路径,比如:org/jni/Test
	jclass propertyClass = env->FindClass(javaClass);
	//<init> 为构造函数,"(J)V" 里的 "J" 代表构造函数需要传一个long类型的参数,"V" 代表方法无返回值,如果构造函数里无参数则()内什么都不写即可,"()V"
	jmethodID init = env->GetMethodID(propertyClass, "<init>", "(J)V");
	jobject obj = env->NewObject(propertyClass, init, 100);
	//也可以通过JNI给java对象的属性设置值,name为Java类的属性名称
	jfieldID name = env->GetFieldID(propertyClass, "name", "Ljava/lang/String;"); // 例如,字段是String类型
    if (name == NULL) {
        return NULL; // 处理错误情况
    }
    env->SetObjectField(obj , name, env->NewStringUTF("张三")); // 设置字段值
    return obj;

3. JNI接收泛型集合和返回集合

C++代码:

extern "C" JNIEXPORT jobject JNICALL Java_org_jni_Test_get(JNIEnv* env, jobject obj, jobject names, jobject persons)
	{
		//需要将jobject persons 转为对应的c++数组集合才可以在C++里进行数据操作
		
		//创建Java集合返回
		jclass listClass = env->FindClass("java/util/ArrayList");
		jmethodID init = env->GetMethodID(listClass, "<init>", "()V");
		jobject javaList = env->NewObject(listClass, init);
		//当前集合的长度为0,可以根据 JNI创建Java对象的代码创建对象添加到集合中
		//jmethodID add = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
		//执行ArrayList的add方法
		//env->CallBooleanMethod(javaList, add, obj);
		return javaList ;
	}

C++数据与Java数据转换参考下面《JNI与C++(Qt)部分数据类型的转换 > java对象集合与C++对象数组互转》例子

Java代码:

创建一个Java对象:

package org.jni;

public class Person {

    private String name;

    private String phone;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
package org.jni;

import java.util.List;

public class Test {

	public native List<Person> get(List<Person> persons);
}

五、JNI与C++(Qt)部分数据类型的转换

基本数据类型可以进行直接使用,不需要进行互相转换。比如:int、float、double、boolean

1. jstring与QString互转

jstring转QString:

QString jstringToQString(JNIEnv* env, jstring jstr) {
	if (!jstr) {
		return QString();
	}
	// 获取Java字符串的UTF-8字符数组
	const char* chars = env->GetStringUTFChars(jstr, nullptr);
	if (!chars) {
		return QString();
	}
	// 将UTF-8字符数组转换为QString
	QString qstr = QString::fromUtf8(chars);

	// 释放Java字符串的UTF-8字符数组
	env->ReleaseStringUTFChars(jstr, chars);
	return qstr;
}

QString转jstring:

jstring qStringToJString(JNIEnv* env, const QString& qstr) {
	// 将QString转换为UTF-8编码的QByteArray
	QByteArray utf8Data = qstr.toUtf8();
	// 将QByteArray转换为C风格的字符串
	const char* cStr = utf8Data.constData();
	// 使用JNIEnv创建jstring
	jstring jstr = env->NewStringUTF(cStr);
	return jstr;
}

2. jstring与std::string互转

jstring转std::string:

#include <jni.h>
#include <string>
std::string jstringToString(JNIEnv* env, jstring jstr) {
    if (env == nullptr || jstr == nullptr) {
        return "";
    }
    
    const char* cstr = env->GetStringUTFChars(jstr, nullptr);
    std::string str(cstr ? cstr : "");
    
    if (cstr) {
        env->ReleaseStringUTFChars(jstr, cstr);
    }
    
    return str;
}

std::string jstringTostring(JNIEnv *env, jstring jStr) {
    if (!jStr) {
        return "";
    }
    jsize length = env->GetStringUTFLength(jStr);
    std::string str(length, '\0');
    env->GetStringUTFRegion(jStr, 0, length, &str[0]);
    
    return str;
}

std::string 转jstring:

#include <jni.h>
#include <string>

jstring stringToJString(JNIEnv* env, const std::string& str) {
    return env->NewStringUTF(str.c_str());
}

//带错误检查
jstring stringToJString(JNIEnv* env, const std::string& str) {
    if (env == nullptr) {
        // 错误处理,如返回nullptr或抛出异常
        return nullptr;
    }
    
    // 检查字符串是否为空
    if (str.empty()) {
        return env->NewStringUTF("");
    }
    
    // 转换并检查是否成功
    jstring result = env->NewStringUTF(str.c_str());
    if (result == nullptr) {
        // 处理内存不足等情况
        env->ExceptionClear(); // 清除可能的异常
        return env->NewStringUTF("");
    }
    
    return result;
}

//处理特殊字符, 如果字符串可能包含非ASCII字符:
jstring stringToJString(JNIEnv* env, const std::string& str) {
    // 先转换为jbyteArray,再转换为jstring
    jbyteArray bytes = env->NewByteArray(str.size());
    env->SetByteArrayRegion(bytes, 0, str.size(), 
                          reinterpret_cast<const jbyte*>(str.c_str()));
    
    jclass stringClass = env->FindClass("java/lang/String");
    jmethodID stringConstructor = env->GetMethodID(
        stringClass, "<init>", "([BLjava/lang/String;)V");
    
    jstring encoding = env->NewStringUTF("UTF-8");
    jstring result = (jstring)env->NewObject(
        stringClass, stringConstructor, bytes, encoding);
    
    env->DeleteLocalRef(bytes);
    env->DeleteLocalRef(encoding);
    
    return result;
}

3. Java枚举与C++枚举对象互转

使用枚举时记得在使用的地方添加自己定义的枚举的头文件

#include "XXXX/XXX/EnumType.h"

自定义枚举文件:
在这里插入图片描述

3.1 数值枚举类型互转

C++代码:

enum PluginType
{
    App = 0,     
    Data = 1,  
    GP = 2
};

Java代码:

package org.jni;

public enum PluginType {
    App (0),   
    Data (1),  
    GP (2);

    private int value;
    private PluginType(int value){
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}

转换代码:

//obj 为Java层传过来的枚举对象,枚举对象的传递与接收和其他对象传递方式一样
PluginType JavaToPluginType(JNIEnv* env, jobject obj)
{
	jclass enumClass = env->FindClass("org/jni/PluginType");
	jfieldID field = env->GetFieldID(enumClass, "value", "I");
	jint value = env->GetIntField(obj, field);
	switch (value) {
	case 1:
		return PluginType::Data;
	case 2:
		return PluginType::GP;
	}
	return PluginType::App;
}
jobject PluginTypeToJavaObject(JNIEnv* env, PluginType type)
{
	jclass enumClass = env->FindClass("org/jni/PluginType");
	jfieldID field = NULL;
	switch (type) {
	case PluginType::App:
		field = env->GetStaticFieldID(enumClass, "App", "Lorg/jni/PluginType;");
		break;
	case PluginType::Data:
		field = env->GetStaticFieldID(enumClass, "Data", "Lorg/jni/PluginType;");
		break;
	case PluginType::GP:
		field = env->GetStaticFieldID(enumClass, "GP", "Lorg/jni/PluginType;");
		break;
	return env->GetStaticObjectField(enumClass, field);
}

3.2 字符串枚举类型互转

C++代码:

enum FieldType
{
    Common,
    Geometric,
    Association,
    Raster
};

Java代码:

package org.jni;

public enum FieldType {
    Common,
    Geometric,
    Association,
    Raster
}

转换代码:

//obj 为Java层传过来的枚举对象,枚举对象的传递与接收和其他对象传递方式一样
FieldType JNIUtils::JavaToFieldType(JNIEnv* env, jobject obj)
{
	jclass enumClass = env->FindClass("org/jni/FieldType");
	jmethodID nameMethod = env->GetMethodID(enumClass, "name", "()Ljava/lang/String;");
	jstring value = (jstring)env->CallObjectMethod(obj, nameMethod);
	const char *chars = env->GetStringUTFChars(value, NULL);
    std::string str(chars);
    env->ReleaseStringUTFChars(value, chars);
	if ("Raster" == str)
	{
		return FieldType::Raster;
	}
	if ("Geometric" == str)
	{
		return FieldType::Geometric;
	}
	if ("Association" == str)
	{
		return FieldType::Association;
	}
	return FieldType::Common;
}
jobject JNIUtils::FieldTypeToJavaObject(JNIEnv* env, FieldType type)
{
	jclass cls = env->FindClass("org/jni/FieldType");
	jfieldID field = NULL;
	switch (type) {
	case FieldType::Common:
		field = env->GetStaticFieldID(cls, "Common", "Lorg/jni/FieldType;");
		break;
	case FieldType::Geometric:
		field = env->GetStaticFieldID(cls, "Geometric", "Lorg/jni/FieldType;");
		break;
	case FieldType::Association:
		field = env->GetStaticFieldID(cls, "Association", "Lorg/jni/FieldType;");
		break;
	case FieldType::Raster:
		field = env->GetStaticFieldID(cls, "Raster", "Lorg/jni/FieldType;");
		break;
	}
	return env->GetStaticObjectField(cls, field);
}

4. String集合(数组)与QStringList互转

Java List<String>转QStringList:

QStringList jobjectArrayToQStringList(JNIEnv* env, jobject jarray)
{
	jclass listClass = env->FindClass("java/util/ArrayList");
	jmethodID get = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
	jmethodID sizeMethod = env->GetMethodID(listClass, "size", "()I");
	jint length = env->CallIntMethod(jarray, sizeMethod);
	
	QStringList list;
	// 遍历 Java 数组
	for (int i = 0; i < length; i++) {
		// 获取数组元素(jstring)
		jstring javaString = (jstring)env->CallObjectMethod(jarray, get, i);
		
		// 将 jstring 转换为 QString
		QString qString = jstringToQString(env, javaString);
		list.append(qString);
	}
	return list;
}

QStringList 转 Java List<String> 或 String[]:

//转String[]
jobjectArray JNIUtils::QStringListToJObjectArray(JNIEnv* env, const QStringList& list) {
	int length = list.size();
	// 创建Java的String数组
	jobjectArray result = env->NewObjectArray(length, env->FindClass("java/lang/String"), nullptr);
	if (!result) return nullptr; // 确保数组创建成功

	// 填充数组
	for (int i = 0; i < length; ++i) {
		const QByteArray byteArray = list.at(i).toUtf8(); // 转换为UTF-8编码的字节数组
		jstring string = env->NewStringUTF(byteArray.constData()); // 转换为jstring
		env->SetObjectArrayElement(result, i, string); // 设置数组元素
		env->DeleteLocalRef(string); // 删除本地引用
	}
	return result;
}

//转List<String>
jobject JNIUtils::CArrayQVariantToJavaArrayList(JNIEnv* env, const QStringList& list)
{
	jclass listClass = env->FindClass("java/util/ArrayList");
	jmethodID init = env->GetMethodID(listClass, "<init>", "()V");
	jobject javaList = env->NewObject(listClass, init);
	jmethodID add = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
	
	for (int i = 0; i < list.size(); i++)
	{
		// 获取数组元素(jstring)
		jstring javaString = (jstring)env->CallObjectMethod(jarray, get, i);
		env->CallBooleanMethod(javaList, add, javaString);
		env->DeleteLocalRef(javaString); // 删除本地引用
	}
	return javaList;
}

5.Java对象与C++对象之间的互转

定义一个Java对象:

package org.jni

public class MyClass {

    /**
     * C++对象指针
     */
    private long ptr;

    public long getCppPointer() {
        return ptr;
    }
    /**
     * 提供给jni调用创建对象
     * @param ptr C++对象指针
     */
    private MyClass(long ptr) {
        this.ptr = ptr;
    }
}

定义一个C++对象:

class MyClass {
public:
	MyClass();
	int getValue();
	void setValue(int value);
private:
	int value;
};

C++转Java对象:

jobject CObjectToJavaObject(JNIEnv* env)
{
	MyClass* myClass= new MyClass();
	jlong ptr = reinterpret_cast<jlong>(cConnectionInfo);
	jclass propertyClass = env->FindClass("org/jni/MyClass");

	jmethodID init = env->GetMethodID(propertyClass, "<init>", "(J)V");
	return env->NewObject(propertyClass, init, ptr);
}

Java对象转C++对象:

//obj 为传递过来的Java MyClass对象,也可以通过env->FindClass("org/jni/MyClass");方式获取
MyClass* getCppObject(JNIEnv* env, jobject obj)
{
	jclass javaClass = env->GetObjectClass(obj);
	jfieldID cppField = env->GetFieldID(javaClass, "ptr", "J");
	if (cppField == NULL) {
		return NULL; // 处理错误情况
	}
	jlong cppPointer = env->GetLongField(obj, cppField);
	MyClass* myClass = reinterpret_cast<MyClass*>(cppPointer);
	return myClass;
}

6.Java double[]与c++ double* 互转

double 转Java double[]*

/// \brief c++ double* 转Java double[]
/// \param [in] cppArray c++ double*
/// \param [in] length c++ double*的长度(元素个数)
jdoubleArray convertToDoubleArray(JNIEnv* env, const double* cppArray, int length)
{
	// 创建一个 jdoubleArray
	jdoubleArray javaArray = env->NewDoubleArray(length);
	if (javaArray == nullptr) {
		return nullptr; // 内存不足时返回 nullptr
	}
	// 将 const double* 的数据复制到 jdoubleArray 中
	env->SetDoubleArrayRegion(javaArray, 0, length, cppArray);
	// 释放 C++ 数组
	delete[] cppArray;
	return javaArray;
}

Java double[]转c++ double*

double* doubleArrayToCDoublePointer(JNIEnv* env, jdoubleArray doubleArray)
{
	// 获取数组长度
	jsize length = env->GetArrayLength(doubleArray);

	// 获取指向数组数据的指针
	jboolean isCopy;
	jdouble* nativeArray = env->GetDoubleArrayElements(doubleArray, &isCopy);
	const double* constArray = nativeArray;
	// 使用 const double* 处理数组
	for (int i = 0; i < length; i++) {
		std::cout << constArray[i] << std::endl;
	}
	// 将 jdouble* 转换为 const double*
	return nativeArray;
}

7.java对象集合与C++对象数组互转

C++写成模板,可对应Java的泛型。

java对象集合转C++对象数组:

这里使用指针来转C++对象,(getCppPointer)为自定义回去C++对象指针的方法,参照《Java对象与C++对象之间的互转》。
#include <vector>

/// \param [in] jarray java对象集合
template<class T>
static std::vector<T*> JavaArrayListToCArray(JNIEnv* env, jobject jarray) {
	jclass listClass = env->FindClass("java/util/ArrayList");
	jmethodID get = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
	jmethodID sizeMethod = env->GetMethodID(listClass, "size", "()I");
	jint length = env->CallIntMethod(jarray, sizeMethod);

	std::vector<T*> cArray;
	// 遍历 Java 数组
	for (int i = 0; i < length; i++) {
		// 获取数组元素
		jobject javaObject = env->CallObjectMethod(jarray, get, i);
		jclass clazz = env->GetObjectClass(javaObject);
		//调用java对象的方法获取对应的c++对象指针,这里使用指针来转C++对象,(getCppPointer)为自定义回去C++对象指针的方法,参照《Java对象与C++对象之间的互转》
		jmethodID getCppPointer = env->GetMethodID(clazz, "getCppPointer", "()J");
		jlong pointer = env->CallLongMethod(javaObject, getCppPointer);
		T* obj = reinterpret_cast<T*>(pointer);
		cArray.push_back(obj);
	}
	return cArray;
}

C++对象数组转java对象集合:

这里使用C++对象转成指针存到Java对象的属性例,参照《Java对象与C++对象之间的互转》。
#include <vector>

/// \param [in] cArray C++对象数组
/// \param [in] javaClass java对象类型(要转的java对象,例:"org/jni/Test")
template<class T>
static jobject CArrayToJavaArrayList(JNIEnv* env, const std::vector<T*>& cArray, const char* javaClass) {
	jclass listClass = env->FindClass("java/util/ArrayList");
	jmethodID init = env->GetMethodID(listClass, "<init>", "()V");
	jmethodID add = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
	jobject javaList = env->NewObject(listClass, init);

	for (int i = 0; i < cArray.size(); i++)
	{
		T* arr = cArray[i];
		jlong ptr = reinterpret_cast<jlong>(arr);
		jclass propertyClass = env->FindClass(javaClass);

		jmethodID init = env->GetMethodID(propertyClass, "<init>", "(J)V");
		jobject obj = env->NewObject(propertyClass, init, ptr);
		//执行ArrayList的add方法
		env->CallBooleanMethod(javaList, add, obj);
		env->DeleteLocalRef(obj); // 删除本地引用
	}
	return javaList;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值