一、JNI基本数据类型
1. 基本数据类型
JNI 提供了与 Java 基本类型对应的本地类型:
Java 类型 | JNI 类型 | C/C++ 类型 | 大小/格式 |
---|---|---|---|
boolean | jboolean | unsigned char | 8 bits |
byte | jbyte | signed char | 8 bits |
char | jchar | unsigned short | 16 bits |
short | jshort | short | 16 bits |
int | jint | int | 32 bits |
long | jlong | long long | 64 bits |
float | jfloat | float | 32 bits |
double | jdouble | double | 64 bits |
2. 引用数据类型
JNI 还定义了一组对应于 Java 对象的引用类型:
JNI 类型 | Java 类型 |
---|---|
jobject | java.lang.Object |
jclass | java.lang.Class |
jstring | java.lang.String |
jarray | 数组 |
jobjectArray | Object[] |
jbooleanArray | boolean[] |
jbyteArray | byte[] |
jcharArray | char[] |
jshortArray | short[] |
jintArray | int[] |
jlongArray | long[] |
jfloatArray | float[] |
jdoubleArray | double[] |
jthrowable | java.lang.Throwable |
3. 类型签名
JNI 使用特定的签名来表示 Java 类型:
Java 类型 | 类型签名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
类类型 (如 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;
}