本文通过一个简单的例子,说明Java如何向C++传递对象类型,C++如何返回对象类型到Java,即Java和C++对象的相互转换。
java层类及native方法定义
com.mm.exmple;
//返回类
class ReturnInfo{
public String host;
public int port;
}
//输入类
class InputInfo{
public String seq;
public int num;
}
//native交互方法
public static native ReturnInfo getInfoFromCPP(InputInfo info);
C++层结构体及方法定义
//返回结构体
struct ReturnInfo
{
std::string host;
int port;
};
//入参结构体
struct InputInfo
{
std::string seq;
int num;
};
ReturnInfo getInfo(InputInfo info)
{
ReturnInfo info;
//do something;
info.host = "127.0.0.1";
info.port = 8080;
return info;
}
JNI层接口定义与实现
//JNI方法声明,需要和Java层的native方法名称、参数、返回值相匹配
JNIEXPORT jobject JNICALL Java_com_mm_example_getInfoFromCPP(JNIEnv *env, jclass cls, jobject info);
//JNI方法实现,完成Java和C++的交互
JNIEXPORT jobject JNICALL
Java_com_mm_example_getInfoFromCPP
(JNIEnv *env, jclass cls, jobject info)
{
//java对象jobject入参转换为C++结构体对象
// 1)获取java InputInfo对象的jclass;
jclass inputInfoCls = env->FindClass("com/mm/example/InputInfo");
// 2)获取java对象的字段ID,注意字段名称和签名;
jfieldID seqId = env->GetFieldID(inputInfoCls, "seq", "Ljava/lang/String;");
// 3)根据字段ID获取该字段的值;
jstring seq = (jstring)env->GetObjectField(info, seqId);
// 其他字段同上;
jfieldID numId = env->GetFieldID(inputInfoCls, "num", "I");
jint num = (jstring)env->GetObjectField(info, numId);
InputInfo inputInfo;
// Java jstring类型转C++ char *类型
char* szSeq = (char*)env->GetStringUTFChars(seq, 0);
inputInfo.seq = szSeq;
env->ReleaseStringUTFChars(seq, szSeq);
// 基础类型可直接赋值;
inputInfo.num = num;
//调用c++方法,得到C++返回结构体对象;
ReturnInfo retInfo = getInfo(InputInfo info);
//C++结构体对象转换为java对象;
// 1)获取java ReturnInfo对象的jclass;
jclass jReturnClass = env->FindClass("com/mm/example/ReturnInfo");
// 2)获取构造方法ID;
jmethodID jmId = env->GetMethodID(jReturnClass, "<init>", "()V");
// 3)通过构造方法ID创建Java ReturnInfo对象;
jobject jReturnObj = env->NewObject(jReturnClass, jmId);
// 4)获取ReturnInfo对象的字段ID;
jfieldID hostId = (env)->GetFieldID(jReturnClass, "host", "Ljava/lang/String;");
jfieldID portId = (env)->GetFieldID(jReturnClass, "port", "I");
// 5)通过字段ID给每个字段赋值
jstring host = env->NewStringUTF(retInfo.host.c_str());
env->SetObjectField(jReturnObj, hostId, host);
env->SetIntField(jReturnObj, portId, (jint)retInfo.port);
// 返回Java对象;
return jReturnObj;
}
至此,就实现了Java对象和C++对象之间的互相转换。
总结
Java对象转C++对象流程
-
获取Java对象的jclass;
-
获取Java对象的字段ID,注意字段名称和签名;
-
根据字段ID获取该字段的值;
-
JNI类型和C++类型转换;
-
对C++对象进行赋值;
C++对象转Java对象流程
-
获取Java对象的jclass;
-
获取构造方法ID;
-
通过构造方法ID创建Java对象;
-
获取Java对象的字段ID;
-
C++类型和JNI类型转换;
-
通过字段ID给每个字段赋值;
Java对象和C++对象之间的互相转换看起来很复杂,其实套路都是固定的,实现起来挺简单的,但麻烦。如果使用比较多的话,可以把重复部分封装成函数,书写和阅读都比较方便。
比如:
获取Java对象的jstring
jstring GetStuStringField(JNIEnv* env, jobject valueObject, jclass clsStu, const char* fieldName)
{
jfieldID idValue = env->GetFieldID(clsStu, fieldName, "Ljava/lang/String;");
return (jstring)env->GetObjectField(valueObject, idValue);
}
其他Java对象的字段可参考这种实现。
jstring转c++ std::string
void CoverStringTostring( JNIEnv* env, jstring jValue, std::string& vValue )
{
if ( jValue == NULL )
{
// log error!
return;
}
char* szValu = (char*)env->GetStringUTFChars(jValue, 0);
if (szValu != NULL) {
vValue = szValu;
} else {
// log error!
}
env->ReleaseStringUTFChars(jValue, szValu);
}
其他类型的转换也可以参考这种实现,封装成函数,方便调用。