现在的位置: 首页 > 综合 > 正文

C++和JNI的数据转换

2013年08月18日 ⁄ 综合 ⁄ 共 7531字 ⁄ 字号 评论关闭

如何使用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
 String name;


    

//
序列号


    
public
 
int
 serial;

}


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


/*
***************** 定义本地方法 *******************
*/



    

//
输入常用的数值类型(Boolean,Byte,Char,Short,Int,Float,Double)


    
public
 native 
void
 displayParms(String showText, 
int
 i, boolean bl);


    

//
调用一个静态方法


    
public
 native 
int
 add(
int
 a, 
int
 b);


    

//
输入一个数组


    
public
 native 
void
 setArray(boolean[] blList);


    

//
返回一个字符串数组


    
public
 native String[] getStringArray();


    

//
返回一个结构


    
public
 native DiskInfo getStruct();


    

//
返回一个结构数组


    
public
 native DiskInfo[] 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数组:

//
返回一个结构数组,返回一个硬盘信息的结构数组


JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray

(JNIEnv 

*
env, jobject _obj)


{

    

//
申明一个object数组 


    jobjectArray args 
=
 
0
;

    

    

//
数组大小


    jsize        len 
=
 
5
;


    

//
获取object所属类,一般为ava/lang/Object就可以了


    jclass objClass 
=
 (env)
->
FindClass(
"
java/lang/Object
"
);


    

//
新建object数组


    args 
=
 (env)
->
NewObjectArray(len, objClass, 
0
);


    


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




    

//
获取Java中的实例类


    jclass objectClass 
=
 (env)
->
FindClass(
"
com/sundy/jnidemo/DiskInfo
"
);

    

    

//
获取类中每一个变量的定义

    

//
名字


    jfieldID str 
=
 (env)
->
GetFieldID(objectClass,
"
name
"
,
"
Ljava/lang/String;
"
);

    

//
序列号


    jfieldID ival 
=
 (env)
->
GetFieldID(objectClass,
"
serial
"
,
"
I
"
);


    

//
给每一个实例的变量付值,并且将实例作为一个object,添加到objcet数组中


    
for
(
int
  i
=
0
; i 
<
 len; i
++
 )

    


{

        

//
给每一个实例的变量付值


        jstring jstr 
=
 WindowsTojstring(env,
"
我的磁盘名字是 D:
"
);

        

//
(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:"));


        (env)
->
SetObjectField(_obj,str,jstr);

        (env)

->
SetShortField(_obj,ival,
10
);


        

//
添加到objcet数组中


        (env)
->
SetObjectArrayElement(args, i, _obj);

    }




    

//
返回object数组


    
return
 args;


 }


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


/*


*

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

*/




/*
*



*  TODO Jni 中一个从Java到C/C++参数传递测试类

*

*  @author 刘正伟(sundy)

*  @see 

http://www.cnweblog.com/sundy


*  @see mailto:sundy26@126.com

*  @version 1.0

*  @since 2005-4-30



*  修改记录:

*  

*  日期              修改人                 描述

*  ----------------------------------------------------------------------------------------------

*

*

*

*/



//
 JniManage.cpp : 定义 DLL 应用程序的入口点。

//

package com.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 –jni javactransfer 生成


#include 
<
stdio.h
>


#include 

"
stdlib.h
"


#include 

"
string.h
"



#pragma comment (lib,

"
BaseInfo.lib
"
)

#pragma comment (lib,

"
jvm.lib
"
)

//
硬盘信息


struct
  

{

    

char
 name[
256
];

    

int
 serial;

}


DiskInfo;


/*
BOOL APIENTRY DllMain( HANDLE hModule, 

                       DWORD  ul_reason_for_call, 

                       LPVOID lpReserved

                     )

{

    LPTSTR  strName = new CHAR[256] ;

    (*GetHostName)(strName);

    printf("%s/n",strName);

    delete [] strName;


    return TRUE;

}

*/



//
将jstring类型转换成windows类型


char
*
 jstringToWindows( JNIEnv 
*
env, jstring jstr );

//
将windows类型转换成jstring类型


jstring WindowsTojstring( JNIEnv
*
 env, 
char
*
 str );


//
主函数


BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved)


{

    

return
 TRUE;

}




//
输入常用的数值类型 Boolean,Byte,Char,Short,Int,Float,Double


JNIEXPORT 
void
 JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_displayParms

(JNIEnv 

*
env, jobject obj, jstring s, jint i, jboolean b)


{

    

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 );

}





//
调用一个静态方法,只有一个简单类型输出


JNIEXPORT jint JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_add

(JNIEnv 

*
env, jobject, jint a, jint b)


{

    

int
 rtn 
=
 (
int
)(a 
+
 b);

    

return
 (jint)rtn;

}






///
/输入一个数组,这里输入的是一个Boolean类型的数组


JNIEXPORT 
void
 JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_setArray

(JNIEnv 

*
env, jobject, jbooleanArray ba)


{

    jboolean

*
 pba 
=
 (env)
->
GetBooleanArrayElements(ba, 
0
 );

    jsize len 

=
 (env)
->
GetArrayLength(ba);

    

int
 i
=
0
;

    

//
 change even array elements


    
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
 );

}






///
/返回一个字符串数组


JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStringArray

(JNIEnv 

*
env, jobject)


{

    jstring      str;

    jobjectArray args 

=

抱歉!评论已关闭.