JNI层与Java层结构体传递

来源:互联网 发布:单片机汇编语言 编辑:程序博客网 时间:2024/06/03 03:34
JNI层与Java层结构体传递
最近在做移动终端开发,需要把native层C++一些统计数据传递给java层使用,在做这部分功能的时候发现JNI层与Java层结构体以及内嵌结构体传递在网上的资料甚少,因此完成功能后做一下这方面的总结,方面后人学习。
  • JNI函数返回一个结构体
  1. 首先我们定义C层的结构体和函数  
    typedef struct _VoiceAPIStat {intnSLESRecOpenSucceedCnt;//open slel mic 创建成功次数intnSLESRecOpenFailedCnt;//open slel mic 创建失败次数intnSLESPlayOpenSucceedCnt;//open slel render 创建成功次数intnSLESPlayOpenFailedCnt;//open slel render 创建失败次数intnJAVARecOpenSucceedCnt;//java mic 创建成功次数intnJAVARecOpenFailedCnt;//java mic 创建失败次数intnJAVAPlayOpenSucceedCnt;//java  render 创建成功次数intnJAVAPlayOpenFailedCnt;//java  render 创建失败次数}VoiceAPIStat;// -------------------------------------------------------------------------------------------------//    系统关键指标数据统计// -------------------------------------------------------------------------------------------------typedef struct _EngRunInfoStat{boolbStatisticsEnable;  <span style="white-space:pre"></span>// 统计是否使能(为假,则不上报)intnStatisticsLevel;// 统计级别(为0,则不统计)intnRptIntervalInMs;// 上报周期(毫秒)intnSpeakMode;//说话模式intnJavaAPILevel;//java api 级别VoiceAPIStatstVoiceApiStat;//音频采集API信息统计}EngRunInfoStat;
    //定义C层获取结构体信息函数
    int  GetRunInfoStat(EngRunInfoStat *pAllStat);
    如代码所示,我们定义一个嵌套的结构体,定义C层提供给JNI层获取EngRunInfoStat结构体参数信息的函数,EngRunInfoStat是一个嵌套结构体,我们最终目的把该C层该结构体值传递到Java层。  
  2. 定义Java层对应的函数和结构体类
    package com.test.eng;public class EngRunInfoStat {public booleanbStatisticsEnable;  // 统计是否使能(为假,则不上报)public intnStatisticsLevel;// 统计级别(为0,则不统计)public intnRptIntervalInMs;// 上报周期(毫秒)public intnSpeakMode;//说话模式public intnJavaAPILevel;//java api 级别public VoiceAPIStatstVoiceApiStat;//音频采集API信息统计public class VoiceAPIStat {public intnSLESRecOpenSucceedCnt;//open slel mic 创建成功次数public intnSLESRecOpenFailedCnt;//open slel mic 创建失败次数public intnSLESPlayOpenSucceedCnt;//open slel render 创建成功次数public intnSLESPlayOpenFailedCnt;//open slel render 创建失败次数public intnJAVARecOpenSucceedCnt;//java mic 创建成功次数public intnJAVARecOpenFailedCnt;//java mic 创建失败次数public intnJAVAPlayOpenSucceedCnt;//java  render 创建成功次数public intnJAVAPlayOpenFailedCnt;//java  render 创建失败次数};};
    //定义对应获取类函数 public final static native EngRunInfoStat GetRunInfoStat();
    如java代码定义对应C层EngRunInfoStat结构体对应的EngRunInfoStat java类,并且定义与jni层通信的native函数,native函数如果与JNI使用不是本节重点,不做额外描述,重点放在结构体传递上。
  3. 通过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);<span style="white-space:pre"></span>}
    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函数传递结构体参数
参数为结构体传递和上面返回值类似,本次先介绍到这里,后面需要的时候在续写参数为结构体情况。
0 0