JNI层与Java层结构体传递

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 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/limingmcu/article/details/83037361