C++和JNI的数据转换

系统 1769 0

如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:
利用VC++6.0实现JNI的最简单的例子
JNI入门教程之HelloWorld篇
SUN JNI Tutorial

这 些资料的例子中,大多数只是输入一些简单的参数,获取没有参数。而在实际的使用过程中,往往需要对参数进行处理转换。才可以被C/C++程序识别。比如我 们在C++中有一个结构(Struct)DiskInfo ,需要传递一个类似于DiskInfo *pDiskInfo的参数,类似于在C++这样参数如何传递到Java中呢?下面我们就来讨论C++到Java中方法的一些常见参数的转换:

定义Native Java类:

如果你习惯了使用JNI,你就不会觉得它难了。既然本地方法是由其他语言实现的,它们在Java中没有函数体。但是,所有本地代码必须用本地关键词声明,成为Java类的成员。假设我们在C++中有这么一个结构,它用来描述硬盘信息:

// 硬盘信息
struct {
char name[ 256 ];
int serial;
}
DiskInfo;

那么我们需要在Java中定义一个类来与之匹配,声明可以写成这样:

class DiskInfo {
// 名字
public Stringname;

// 序列号
public int serial;
}

在这个类中,申明一些Native的本地方法,来测试方法参数的传递,分别定义了一些函数,用来传递结构或者结构数组,具体定义如下面代码:

/**/ /* *****************定义本地方法******************* */
// 输入常用的数值类型(Boolean,Byte,Char,Short,Int,Float,Double)
public native void displayParms(StringshowText, int i,booleanbl);

// 调用一个静态方法
public native int add( int a, int b);

// 输入一个数组
public native void setArray(boolean[]blList);

// 返回一个字符串数组
public nativeString[]getStringArray();

// 返回一个结构
public nativeDiskInfogetStruct();

// 返回一个结构数组
public nativeDiskInfo[]getStructArray();

编译生成C/C++头文件

定义好了Java类之后,接下来就要写本地代码。本地方法符号提供一个满足约定的头文件,使用 Java工具Javah 可以很容易地创建它而不用手动去创建。你对Java的class文件使用javah命令,就会为你生成一个对应的C/C++头文件。
1、在控制台下进入工作路径,本工程路径为:E:\work\java\workspace\JavaJni。
2、运行javah 命令:javah -classpath E:\work\java\workspace\JavaJni com.sundy.jnidemo ChangeMethodFromJni
本文生成的C/C++头文件名为: com_sundy_jnidemo_ChangeMethodFromJni.h


在C/C++中实现本地方法

生成C/C++头文件之后,你就需要写头文件对应的本地方法。注意:所有的本地方法的第一个参数都是指向 JNIEnv 结构的。这个结构是用来调用JNI函数的。第二个参数jclass的意义,要看方法是不是静态的(static)或者实例(Instance)的。前者,jclass代表一个类对象的引用,而后者是被调用的方法所属对象的引用。

返回值和参数类型 根据等价约定映射到本地C/C++类型,如表 JNI类型映射 所示。有些类型,在本地代码中可直接使用,而其他类型只有通过JNI调用操作。

表A

Java 类型 本地类型 描述
boolean jboolean C/C++8位整型
byte jbyte C/C++带符号的8位整型
char jchar C/C++无符号的16位整型
short jshort C/C++带符号的16位整型
int jint C/C++带符号的32位整型
long jlong C/C++带符号的64位整型e
float jfloat C/C++32位浮点型
double jdouble C/C++64位浮点型
Object jobject 任何Java对象,或者没有对应java类型的对象
Class jclass Class对象
String jstring 字符串对象
Object[] jobjectArray 任何对象的数组
boolean[] jbooleanArray 布尔型数组
byte[] jbyteArray 比特型数组
char[] jcharArray 字符型数组
short[] jshortArray 短整型数组
int[] jintArray 整型数组
long[] jlongArray 长整型数组
float[] jfloatArray 浮点型数组
double[] jdoubleArray 双浮点型数组

JNI类型映射

使用数组:

JNI通过JNIEnv提供的操作Java数组的功能。它提供了两个函数:一个是操作java的简单型数组的,另一个是操作对象类型数组的。

因为速度的原因,简单类型的数组作为指向本地类型的指针暴露给本地代码。因此,它们能作为常规的数组存取。这个指针是指向实际的Java数组或者Java数组的拷贝的指针。另外,数组的布置保证匹配本地类型。

为了存取Java简单类型的数组,你就要要使用 GetXXXArrayElements 函数(见 B ),XXX代表了数组的类型。这个函数把Java数组看成参数,返回一个指向对应的本地类型的数组的指针。

表B

函数 Java 数组类型 本地类型
GetBooleanArrayElements jbooleanArray jboolean
GetByteArrayElements jbyteArray jbyte
GetCharArrayElements jcharArray jchar
GetShortArrayElements jshortArray jshort
GetIntArrayElements jintArray jint
GetLongArrayElements jlongArray jlong
GetFloatArrayElements jfloatArray jfloat
GetDoubleArrayElements jdoubleArray jdouble

JNI数组存取函数

当你对数组的存取完成后,要确保调用相应的 ReleaseXXXArrayElements 函数,参数是对应Java数组和 GetXXXArrayElements 返回的指针。如果必要的话,这个释放函数会复制你做的任何变化(这样它们就反射到java数组),然后释放所有相关的资源。

为了使用java对象的数组,你必须使用 GetObjectArrayElement 函数和 SetObjectArrayElement 函数,分别去get,set数组的元素。 GetArrayLength 函数会返回数组的长度。

使用对象

JNI提供的另外一个功能是在本地代码中使用Java对象。通过使用合适的JNI函数,你可以创建 Java对象,get、set 静态(static)和实例(instance)的域,调用静态(static)和实例(instance)函数。JNI通过ID识别域和方法,一个域或 方法的ID是任何处理域和方法的函数的必须参数。

表C 列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数。每个函数接受(作为参数)域或方法的类,它们的名称,符号和它们对应返回的 jfieldID jmethodID

表C

函数 描述
GetFieldID 得到一个实例的域的ID
GetStaticFieldID 得到一个静态的域的ID
GetMethodID 得到一个实例的方法的ID
GetStaticMethodID 得到一个静态方法的ID

※域和方法的函数

如果你有了一个类的实例,它就可以通过方法 GetObjectClass 得到,或者如果你没有这个类的实例,可以通过 FindClass 得到。符号是从域的类型或者方法的参数,返回值得到字符串,如 D 所示。

表D

Java 类型 符号
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V
objects对象 Lfully-qualified-class-name;L类名
Arrays数组 [array-type [数组类型
methods方法 (argument-types)return-type(参数类型)返回类型

※确定域和方法的符号


下面我们来看看,如果通过使用数组和对象,从C++中的获取到Java中的DiskInfo 类对象,并返回一个DiskInfo数组:

// 返回一个结构数组,返回一个硬盘信息的结构数组
JNIEXPORTjobjectArrayJNICALLJava_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray
(JNIEnv
* env,jobject_obj)
{
// 申明一个object数组
jobjectArrayargs = 0 ;

// 数组大小
jsizelen = 5 ;

// 获取object所属类,一般为ava/lang/Object就可以了
jclassobjClass = (env) -> FindClass( " java/lang/Object " );

// 新建object数组
args = (env) -> NewObjectArray(len,objClass, 0 );

/**/ /* 下面为获取到Java中对应的实例类中的变量 */

// 获取Java中的实例类
jclassobjectClass = (env) -> FindClass( " com/sundy/jnidemo/DiskInfo " );

// 获取类中每一个变量的定义
// 名字
jfieldIDstr = (env) -> GetFieldID(objectClass, " name " , " Ljava/lang/String; " );
// 序列号
jfieldIDival = (env) -> GetFieldID(objectClass, " serial " , " I " );

// 给每一个实例的变量付值,并且将实例作为一个object,添加到objcet数组中
for ( int i = 0 ;i < len;i ++ )
{
// 给每一个实例的变量付值
jstringjstr = WindowsTojstring(env, " 我的磁盘名字是D: " );
// (env)->SetObjectField(_obj,str,(env)->NewStringUTF("mynameisD:"));
(env) -> SetObjectField(_obj,str,jstr);
(env)
-> SetShortField(_obj,ival, 10 );

// 添加到objcet数组中
(env) -> SetObjectArrayElement(args,i,_obj);
}

// 返回object数组
return args;

}



全部的C/C++方法实现代码如下:

/**/ /*
*
*一缕阳光(sundy)版权所有,保留所有权利。
*/

/**/ /* *
*
*TODOJni中一个从Java到C/C++参数传递测试类
*
*@author刘正伟(sundy)
*@see
http://www.cnweblog.com/sundy
*@seemailto:sundy26@126.com
*@version1.0
*@since2005-4-30
*
*修改记录:
*
*日期修改人描述
*----------------------------------------------------------------------------------------------
*
*
*
*/

// JniManage.cpp:定义DLL应用程序的入口点。
//
packagecom.sundy.jnidemo;
#include
" stdafx.h "

#include
< stdio.h >
#include
< math.h >
#include
" jni.h "
#include
" jni_md.h "

#include
" ./head/Base.h "
#include
" head/wmi.h "
#include
" head/com_sundy_jnidemo_ChangeMethodFromJni.h " // 通过javah–jnijavactransfer生成
#include < stdio.h >
#include
" stdlib.h "
#include
" string.h "

#pragmacomment(lib,
" BaseInfo.lib " )
#pragmacomment(lib,
" jvm.lib " )
// 硬盘信息
struct {
char name[ 256 ];
int serial;
}
DiskInfo;
/**/ /* BOOLAPIENTRYDllMain(HANDLEhModule,
DWORDul_reason_for_call,
LPVOIDlpReserved
)
{
LPTSTRstrName=newCHAR[256];
(*GetHostName)(strName);
printf("%s\n",strName);
delete[]strName;

returnTRUE;
}
*/

// 将jstring类型转换成windows类型
char * jstringToWindows(JNIEnv * env,jstringjstr);
// 将windows类型转换成jstring类型
jstringWindowsTojstring(JNIEnv * env, char * str);

// 主函数
BOOLWINAPIDllMain(HANDLEhHandle,DWORDdwReason,LPVOIDlpReserved)
{
return TRUE;
}

// 输入常用的数值类型Boolean,Byte,Char,Short,Int,Float,Double
JNIEXPORT void JNICALLJava_com_sundy_jnidemo_ChangeMethodFromJni_displayParms
(JNIEnv
* env,jobjectobj,jstrings,jinti,jbooleanb)
{
const char * szStr = (env) -> GetStringUTFChars(s, 0 );
printf(
" String=[%s]\n " ,szStr);
printf(
" int=%d\n " ,i);
printf(
" boolean=%s\n " ,(b == JNI_TRUE ? " true " : " false " ));
(env)
-> ReleaseStringUTFChars(s,szStr);
}


// 调用一个静态方法,只有一个简单类型输出
JNIEXPORTjintJNICALLJava_com_sundy_jnidemo_ChangeMethodFromJni_add
(JNIEnv
* env,jobject,jinta,jintb)
{
int rtn = ( int )(a + b);
return (jint)rtn;
}


/**/ /// /输入一个数组,这里输入的是一个Boolean类型的数组
JNIEXPORT void JNICALLJava_com_sundy_jnidemo_ChangeMethodFromJni_setArray
(JNIEnv
* env,jobject,jbooleanArrayba)
{
jboolean
* pba = (env) -> GetBooleanArrayElements(ba, 0 );
jsizelen
= (env) -> GetArrayLength(ba);
int i = 0 ;
// changeevenarrayelements
for (i = 0 ;i < len;i += 2 )
{
pba[i]
= JNI_FALSE;
printf(
" boolean=%s\n " ,(pba[i] == JNI_TRUE ? " true " : " false " ));
}

(env)
-> ReleaseBooleanArrayElements(ba,pba, 0 );
}


/**/ /// /返回一个字符串数组
JNIEXPORTjobjectArrayJNICALLJava_com_sundy_jnidemo_ChangeMethodFromJni_getStringArray
(JNIEnv
* env,jobject)
{
jstringstr;
jobjectArrayargs
= 0 ;
jsizelen
= 5 ;
char * sa[] = { " Hello, " , " world! " , " JNI " , " is " , " <spa
分享到:
评论

C++和JNI的数据转换


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论