JNI层与Java层结构体传递

JNI层与Java层结构体传递
最近在做移动终端开发,需要把native层C++一些统计数据传递给java层使用,在做这部分功能的时候发现JNI层与Java层结构体以及内嵌结构体传递在网上的资料甚少,因此完成功能后做一下这方面的总结,方面后人学习。
  • JNI函数返回一个结构体
  1. 首先我们定义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层。  
  2. 定义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使用不是本节重点,不做额外描述,重点放在结构体传递上。
  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函数传递结构体参数
参数为结构体传递和上面返回值类似,本次先介绍到这里,后面需要的时候在续写参数为结构体情况。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值