JNI层与Java层结构体传递
最近在做移动终端开发,需要把native层C++一些统计数据传递给java层使用,在做这部分功能的时候发现JNI层与Java层结构体以及内嵌结构体传递在网上的资料甚少,因此完成功能后做一下这方面的总结,方面后人学习。
JNI函数返回一个结构体
首先我们定义C层的结构体和函数
typedef struct _VoiceAPIStat
{
int nSLESRecOpenSucceedCnt; //open slel mic 创建成功次数
int nSLESRecOpenFailedCnt; //open slel mic 创建失败次数
int nSLESPlayOpenSucceedCnt; //open slel render 创建成功次数
int nSLESPlayOpenFailedCnt; //open slel render 创建失败次数
int nJAVARecOpenSucceedCnt; //java mic 创建成功次数
int nJAVARecOpenFailedCnt; //java mic 创建失败次数
int nJAVAPlayOpenSucceedCnt; //java render 创建成功次数
int nJAVAPlayOpenFailedCnt; //java render 创建失败次数
}VoiceAPIStat;
// -------------------------------------------------------------------------------------------------
// 系统关键指标数据统计
// -------------------------------------------------------------------------------------------------
typedef struct _EngRunInfoStat
{
bool bStatisticsEnable; <span style="white-space:pre"> </span>// 统计是否使能(为假,则不上报)
int nStatisticsLevel; // 统计级别(为0,则不统计)
int nRptIntervalInMs; // 上报周期(毫秒)
int nSpeakMode; //说话模式
int nJavaAPILevel; //java api 级别
VoiceAPIStat stVoiceApiStat; //音频采集API信息统计
}EngRunInfoStat;
//定义C层获取结构体信息函数
int GetRunInfoStat(EngRunInfoStat *pAllStat);
如代码所示,我们定义一个嵌套的结构体,定义C层提供给JNI层获取EngRunInfoStat结构体参数信息的函数,EngRunInfoStat是一个嵌套结构体,我们最终目的把该C层该结构体值传递到Java层。
定义Java层对应的函数和结构体类
package com.test.eng;
public class EngRunInfoStat {
public boolean bStatisticsEnable; // 统计是否使能(为假,则不上报)
public int nStatisticsLevel; // 统计级别(为0,则不统计)
public int nRptIntervalInMs; // 上报周期(毫秒)
public int nSpeakMode; //说话模式
public int nJavaAPILevel; //java api 级别
public VoiceAPIStat stVoiceApiStat; //音频采集API信息统计
public class VoiceAPIStat {
public int nSLESRecOpenSucceedCnt; //open slel mic 创建成功次数
public int nSLESRecOpenFailedCnt; //open slel mic 创建失败次数
public int nSLESPlayOpenSucceedCnt; //open slel render 创建成功次数
public int nSLESPlayOpenFailedCnt; //open slel render 创建失败次数
public int nJAVARecOpenSucceedCnt; //java mic 创建成功次数
public int nJAVARecOpenFailedCnt; //java mic 创建失败次数
public int nJAVAPlayOpenSucceedCnt; //java render 创建成功次数
public int nJAVAPlayOpenFailedCnt; //java render 创建失败次数
};
};
//定义对应获取类函数
public final static native EngRunInfoStat GetRunInfoStat();
如java代码定义对应C层EngRunInfoStat结构体对应的EngRunInfoStat java类,并且定义与jni层通信的native函数,native函数如果与JNI使用不是本节重点,不做额外描述,重点放在结构体传递上。
通过JNI层实现C层与Java层结构体转换 JNI层的代码实现是最关键的,首先我们要获取对应Java层定义的EngRunInfoStat类,并且需要获取该类的构造函数,然后通过NewObject函数创建该类的一个实例,注意我们EngRunInfoStat类里面还包含一个内嵌类,通过NewObject创建EngRunInfoStat类并没有包含内嵌类的实例,内嵌类我们后面还需单独的创建,先看示例代码
//获取Java实例
jclass objectClass = (env)->FindClass("com/test/eng/EngRunInfoStat");
//获取成员变量
jmethodID objectClassInitID = (env)->GetMethodID(objectClass, "<init>", "()V");
jobject objectNewEng = (env)->NewObject(objectClass, objectClassInitID);
objectNewEng即是我们新创建出来EngRunInfoStat实例,为了给类成员变量赋值,我们还需要将类中成员变量ID导出来,用于后续赋值操作
jfieldID bStatisticsEnable = (env)->GetFieldID(objectClass, "bStatisticsEnable", "Z");
jfieldID nStatisticsLevel = (env)->GetFieldID(objectClass, "nStatisticsLevel", "I");
jfieldID nRptIntervalInMs = (env)->GetFieldID(objectClass, "nRptIntervalInMs", "I");
jfieldID nSpeakMode = (env)->GetFieldID(objectClass, "nSpeakMode", "I");
jfieldID nJavaAPILevel = (env)->GetFieldID(objectClass, "nJavaAPILevel", "I");
jfieldID stVoiceApiStat = (env)->GetFieldID(objectClass, "stVoiceApiStat", "Lcom/test/eng/EngRunInfoStat$VoiceAPIStat;");
如代码所示,普通成员变量jfiledID通过GetFieldID(),根据变量名称和类型签名导出即可,关键是成员中内嵌类导出比较特殊,stVoiceApiStat是一个内嵌类成员变量它是VoiceAPIStat内嵌类,内嵌类的签名比较特殊,首先是L开头代表类,然后包名,外部类与内部类签名之间用$隔开,即“Lcom/test/eng/EngRunInfoStat$VoiceAPIStat;”,获取到成员变量字段ID后,对于一般成员变量,我们就可以对其赋值了,示例如下:
if(0 == GetRunInfoStat(&stEngInfoStat))
{
(env)->SetBooleanField(objectNewEng, bStatisticsEnable, (jboolean)stEngInfoStat.bStatisticsEnable);
(env)->SetIntField(objectNewEng, nStatisticsLevel, stEngInfoStat.nStatisticsLevel);
(env)->SetIntField(objectNewEng, nRptIntervalInMs, stEngInfoStat.nRptIntervalInMs);
(env)->SetIntField(objectNewEng, nSpeakMode, stEngInfoStat.nSpeakMode);
(env)->SetIntField(objectNewEng, nJavaAPILevel, stEngInfoStat.nJavaAPILevel);
}
GetRunInfoStat是C层获取C结构体变量的函数,stEngInfoStat是获取数据后保存的变量,获取数据成功后就可以将每个值设置到对应java类成员变量中,即通过SetIntField()函数设置,用法见示例代码,可以看到我们还没有对内嵌类stVoiceApiStat进行操作,这是因为对内嵌类的操作比较特殊,与普通成员变量需要区别对待,对于内嵌类我们首先要导出内嵌类对象,然后到处内嵌类构造函数,接着用NewObject创建内嵌类的实例,示例代码如下:
jclass obVoiceAPI = (env)->FindClass("com/qq/qtx/EngRunInfoStat$VoiceAPIStat");
jmethodID objectVoiceInitID = (env)->GetMethodID(obRecordMode, "<init>", "(Lcom/qq/qtx/EngRunInfoStat;)V");
jobject objectVoice = (env)->NewObject(obVoiceAPI, objectVoiceInitID, objectNewEng);
jfieldID nSLESRecOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESRecOpenSucceedCnt", "I");
jfieldID nSLESRecOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESRecOpenFailedCnt", "I");
jfieldID nSLESPlayOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESPlayOpenSucceedCnt", "I");
jfieldID nSLESPlayOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESPlayOpenFailedCnt", "I");
jfieldID nJAVARecOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVARecOpenSucceedCnt", "I");
jfieldID nJAVARecOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVARecOpenFailedCnt", "I");
jfieldID nJAVAPlayOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVAPlayOpenSucceedCnt", "I");
jfieldID nJAVAPlayOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVAPlayOpenFailedCnt", "I");
创建实例后,就可以给内嵌类的成员变量赋值操作了,示例代码如下:
<span style="white-space:pre"> </span>(env)->SetIntField(objectVoice, nSLESRecOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nSLESRecOpenSucceedCnt);
(env)->SetIntField(objectVoice, nSLESRecOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nSLESRecOpenFailedCnt);
(env)->SetIntField(objectVoice, nSLESPlayOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nSLESPlayOpenSucceedCnt);
(env)->SetIntField(objectVoice, nSLESPlayOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nSLESPlayOpenFailedCnt);
(env)->SetIntField(objectVoice, nJAVARecOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nJAVARecOpenSucceedCnt);
(env)->SetIntField(objectVoice, nJAVARecOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nJAVARecOpenFailedCnt);
(env)->SetIntField(objectVoice, nJAVAPlayOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nJAVAPlayOpenSucceedCnt);
(env)->SetIntField(objectVoice, nJAVAPlayOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nJAVAPlayOpenFailedCnt);
赋值完成后,我们还需要将创建内嵌类ObjectVoice用SetObjectField()函数设置到创建到外部类中,这样外部类中内嵌类才起作用是有效的,示例代码如下:
(env)->SetObjectField(objectNewEng, stVoiceApiStat, objectVoice);
至此JNI层函数里面对于Java结构体类就创建赋值完成了,最后通过函数返回给Java层就可以了,JNI函数完成代码示例如下:
/*
* Class: com_test_eng_jni_NativeMethodJNI
* Method: GetRunInfoStat
* Signature: ()Lcom/test/eng/EngRunInfoStat;
*/
JNIEXPORT jobject JNICALL Java_com_test_eng_jni_NativeMethodJNI_GetRunInfoStat(JNIEnv *env, jclass obj)
{
EngRunInfoStat stEngInfoStat;
memset(&stEngInfoStat, 0, sizeof(EngRunInfoStat));
//获取Java实例
jclass objectClass = (env)->FindClass("com/tess/eng/EngRunInfoStat");
//获取成员变量
jmethodID objectClassInitID = (env)->GetMethodID(objectClass, "<init>", "()V");
jobject objectNewEng = (env)->NewObject(objectClass, objectClassInitID);
jfieldID bStatisticsEnable = (env)->GetFieldID(objectClass, "bStatisticsEnable", "Z");
jfieldID nStatisticsLevel = (env)->GetFieldID(objectClass, "nStatisticsLevel", "I");
jfieldID nRptIntervalInMs = (env)->GetFieldID(objectClass, "nRptIntervalInMs", "I");
jfieldID nSpeakMode = (env)->GetFieldID(objectClass, "nSpeakMode", "I");
jfieldID nJavaAPILevel = (env)->GetFieldID(objectClass, "nJavaAPILevel", "I");
jfieldID stVoiceApiStat = (env)->GetFieldID(objectClass, "stVoiceApiStat", "Lcom/test/eng/EngRunInfoStat$VoiceAPIStat;");
jclass obVoiceAPI = (env)->FindClass("com/qq/qtx/EngRunInfoStat$VoiceAPIStat");
jmethodID objectVoiceInitID = (env)->GetMethodID(obRecordMode, "<init>", "(Lcom/test/eng/EngRunInfoStat;)V");
jobject objectVoice = (env)->NewObject(obVoiceAPI, objectVoiceInitID, objectNewEng);
jfieldID nSLESRecOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESRecOpenSucceedCnt", "I");
jfieldID nSLESRecOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESRecOpenFailedCnt", "I");
jfieldID nSLESPlayOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESPlayOpenSucceedCnt", "I");
jfieldID nSLESPlayOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nSLESPlayOpenFailedCnt", "I");
jfieldID nJAVARecOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVARecOpenSucceedCnt", "I");
jfieldID nJAVARecOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVARecOpenFailedCnt", "I");
jfieldID nJAVAPlayOpenSucceedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVAPlayOpenSucceedCnt", "I");
jfieldID nJAVAPlayOpenFailedCnt = (env)->GetFieldID(obVoiceAPI, "nJAVAPlayOpenFailedCnt", "I");
if(0 == GetRunInfoStat(&stEngInfoStat))
{
(env)->SetBooleanField(objectNewEng, bStatisticsEnable, (jboolean)stEngInfoStat.bStatisticsEnable);
(env)->SetIntField(objectNewEng, nStatisticsLevel, stEngInfoStat.nStatisticsLevel);
(env)->SetIntField(objectNewEng, nRptIntervalInMs, stEngInfoStat.nRptIntervalInMs);
(env)->SetIntField(objectNewEng, nSpeakMode, stEngInfoStat.nSpeakMode);
(env)->SetIntField(objectNewEng, nJavaAPILevel, stEngInfoStat.nJavaAPILevel);
(env)->SetIntField(objectVoice, nSLESRecOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nSLESRecOpenSucceedCnt);
(env)->SetIntField(objectVoice, nSLESRecOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nSLESRecOpenFailedCnt);
(env)->SetIntField(objectVoice, nSLESPlayOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nSLESPlayOpenSucceedCnt);
(env)->SetIntField(objectVoice, nSLESPlayOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nSLESPlayOpenFailedCnt);
(env)->SetIntField(objectVoice, nJAVARecOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nJAVARecOpenSucceedCnt);
(env)->SetIntField(objectVoice, nJAVARecOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nJAVARecOpenFailedCnt);
(env)->SetIntField(objectVoice, nJAVAPlayOpenSucceedCnt, stEngInfoStat.stVoiceApiStat.nJAVAPlayOpenSucceedCnt);
(env)->SetIntField(objectVoice, nJAVAPlayOpenFailedCnt, stEngInfoStat.stVoiceApiStat.nJAVAPlayOpenFailedCnt);
}
(env)->SetObjectField(objectNewEng, stVoiceApiStat, objectVoice);
return objectNewEng;
}
Java函数传递结构体参数
---------------------
作者:ruoshui_dy
来源:CSDN
原文:https://blog.csdn.net/zdy_ruoshui/article/details/44132071?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!