JNI中实现类似C++回调方法-结构体操作

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

上篇文章讲述了JNI中C++简单的调用Java函数,接下来我们要解决上次留下的疑惑问题进行解答,如何调用带有结构体的java函数。

假设:C++要给Java传入一个叫做DataSt的结构体,那么,Android也需要定义同样的结构体,保持与so文件中的结构体名称以及字段保持一致,否则查询不到。

结构体内容如下:

struct DataSt
{
    std::string id;
    std::vector<int> vetIndex;
}
复制代码

C++传入的数据保存在:std::vector m_vetArray中,在java中是如何接收带有STL的结构体数据呢?

接下来我会逐步进行讲解~

调用讲解

1:定义Java结构体

为了让java代码中获取到C++调用的数据时,此时JAVA中需要定义这样一个DataSt的结构体。

查询JAVA中定义结构体的位置

jclass objClass = mJni->FindClass("cn/test/model/java2so/event/DataSt");
复制代码

注意此时FindClass对应的就是Java中定义结构体的程序路径,一定要写全,否则无法查询到!

2:创建一个java数组对象

与C++方法一样,想要把数据存储到容器中,需要定义一个这样的容器,这里需要明确容器的大小,也就是需要传给JAVA数据的大小,类似于C++的数组。

int nsize = 10; //假设,需要给java传入10组结构体数据,这里根据实际情况自行定义
//创建:数组对象
jobjectArray arrayData = mJni->NewObjectArray(nsize, objClass, NULL);
if(arrayData == nullptr)
{
    //此时,说明new出来的数组无效
    return;
}
复制代码

3:设置JAVA方法

结构中存在了两种变量类型:string、int

获取id方法ID

jmethodID method_id = mJni->GetMethodID(objClass, "setId", "(Ljava/lang/String;)V");
复制代码

获取vetIndex中一条int值的方法ID

jmethodID method_index = mJni->GetMethodID(objClass, "setIndex", "(I)V");
复制代码

如果这里不知道如何获取方法,可以结合java结构体自动生成的函数一一对应。

4:类型转换

想要让Java识别C++的数据,我们就需要对类型进行转换,依据DataSt中的两种结构类型进行处理转换

4.1:string -> jstring

假设:string id = "123";

jstring jname = mJni->NewStringUTF(str.c_str());
复制代码

4.2:int类型

注意:在jni中,int类型是直接可以赋值的,不需要特殊处理

5:遍历方式存储成java结构

这里到了我们存储的重点了,上面的内容都是为这一步做的铺垫。

在第二步骤时,我们假设了C++容器的大小为10,那么,我们这里依旧用10做处理

int nsize = 10;

我们在进行数据存储是,是按照顺序的方式存储,首先我们要定义一个下标方法。

jmethodID method_init_Event = mJni->GetMethodID(objClass, "<init>", "()V");
复制代码

在进行数据存储时,需要用到这个method_init_Event参数的

在C++中插入一条数据时,例如vector直接push_back方法就可以了,或者也可以使用insert方法。

下面,先将存储的大体框架展示出来

for (int i = 0; i < nsize; i++)
{
    DataSt stInfo = m_vetArray[i]; //C++结构体中的一条数据
    //1:创建一个对象用于存储一条数据
    jobject objmodel = mJni->NewObject(objClass, method_init_Event, "");
    
    //2:赋值操作
    //2.1:将id数据设置到objmodel对象中
    //2.2:将vetIndex数据设置到objmodel对象中
    
    //3:将数据设置到Java的Arrary中
    mJni->SetObjectArrayElement(arrayData, i, objmodel); 
}
复制代码

以上就是粗略的赋值流程了,针对于2操作,我们进行具体的代码展示吧!

操作2.1:将id数据设置到objmodel对象中

第一步:string转成jstring类型

jstring jsId = mJni->NewStringUTF(stInfo.id.c_str());
复制代码

第二步:存储到objmodel对象中

mJni->CallVoidMethod(objmodel, method_id, jname);
复制代码

操作2.2:将vetIndex数据设置到objmodel对象中

for(int n = 0; n < stInfo.vetIndex.size(); n++)
{
    int nIndex = stInfo.vetIndex[n];
    mJni->CallVoidMethod(objmodel, method_index, nIndex);
}
复制代码

6:C++调用Java函数传递参数

第五步骤中已经将数据存储到结构体中了,那么这里开始实际调用JAVA的函数了。

假设,这里被调用的函数名叫做:RecordDataAndroidBack(数组,bool类型)

jmethodID mid;
mid = mJni->GetMethodID(cls, "RecordDataAndroidBack", "([Lcn/test/model/java2so/event/DataSt;Z)V");
if(mid == nullptr)
{
    //函数调用错误!
    return;
}
//调用成功后,将容器arrayData中的数据传入函数汇总
mJni->CallVoidMethod(mThiz, mid, arrayData, bres);
复制代码

代码解析:

出现函数调用错误时,最容易出现的问题就是JAVA函数参数的路径写的不对,或者是格式不正确,90%都是在第三个参数上出问题的,而且,有些问题还会跑偏,让你抓狂,让你无处可找!!

到这里,如何调用带有结构体容器的JAVA函数已经讲解完成了,小伙伴们尽情的尝试吧!

我是中国好公民st,一名C++开发程序猿~

猜你喜欢

转载自juejin.im/post/7102361188113055752