jni中参数的传递与操作 -凯发k8官方网
jni中c 和java的参数传递
如何使用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;
//序列号
publicint 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)凯发k8官方网的版权所有,保留所有权利。
*/
/**
*
* 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)
{
constchar* 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 =0;
jsize len =5;
char* sa[] ={ "hello,", "world!", "jni", "is", "fun" };
int i=0;
args = (env)->newobjectarray(len,(env)->findclass("java/lang/string"),0);
for( i=0; i < len; i )
{
str = (env)->newstringutf(sa[i] );
(env)->setobjectarrayelement(args, i, str);
}
return args;
}
//返回一个结构,这里返回一个硬盘信息的简单结构类型
jniexport jobject jnicall java_com_sundy_jnidemo_changemethodfromjni_getstruct
(jnienv *env, jobject obj)
{
/* 下面为获取到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");
//给每一个实例的变量付值
(env)->setobjectfield(obj,str,(env)->newstringutf("my name is d:"));
(env)->setshortfield(obj,ival,10);
return obj;
}
//返回一个结构数组,返回一个硬盘信息的结构数组
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;
}
//将jstring类型转换成windows类型
char* jstringtowindows( jnienv *env, jstring jstr )
{
int length = (env)->getstringlength(jstr );
const jchar* jcstr = (env)->getstringchars(jstr, 0 );
char* rtn = (char*)malloc( length*21 );
int size =0;
size = widechartomultibyte( cp_acp, 0, (lpcwstr)jcstr, length, rtn,(length*21), null, null );
if( size <=0 )
return null;
(env)->releasestringchars(jstr, jcstr );
rtn[size] =0;
return rtn;
}
//将windows类型转换成jstring类型
jstring windowstojstring( jnienv* env, char* str )
{
jstring rtn =0;
int slen = strlen(str);
unsigned short* buffer =0;
if( slen ==0 )
rtn = (env)->newstringutf(str );
else
{
int length = multibytetowidechar( cp_acp, 0, (lpcstr)str, slen, null, 0 );
buffer = (unsigned short*)malloc( length*21 );
if( multibytetowidechar( cp_acp, 0, (lpcstr)str, slen, (lpwstr)buffer, length ) >0 )
rtn = (env)->newstring( (jchar*)buffer, length );
}
if( buffer )
free( buffer );
return rtn;
}
java 测试native代码
这没有什么多说的,看代码吧
//主测试程序
publicstaticvoid main(string[] args) {
changemethodfromjni changejni =new changemethodfromjni();
//输入常用的数值类型(string int boolean)
system.out
.println("------------------输入常用的数值类型(string int boolean)-----------");
changejni.displayparms("hello world!", 100, true);
//调用一个静态方法
system.out.println("------------------调用一个静态方法-----------");
int ret = changejni.add(12, 20);
system.out.println("the result is: " string.valueof(ret));
//输入一个数组
system.out.println("------------------输入一个数组-----------");
boolean[] bllist =new boolean[] { true, false, true };
changejni.setarray(bllist);
//返回一个字符串数组
system.out.println("------------------返回一个字符串数组-----------");
string[] strlist = changejni.getstringarray();
for (int i =0; i < strlist.length; i) {
system.out.print(strlist[i]);
}
system.out.println();
system.out.println("------------------返回一个结构-----------");
//返回一个结构
diskinfo disk = changejni.getstruct();
system.out.println("name:" disk.name);
system.out.println("serial:" disk.serial);
//返回一个结构数组
system.out.println("------------------返回一个结构数组 -----------");
diskinfo[] disklist = changejni.getstructarray();
for (int i =0; i < disklist.length; i) {
system.out.println("name:" disklist[i].name);
system.out.println("serial:" disklist[i].serial);
}
}
转载于:https://blog.51cto.com/laokaddk/1343401
与50位技术专家面对面20年技术见证,附赠技术全景图总结
以上是凯发k8官方网为你收集整理的jni中参数的传递与操作的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇:
- 下一篇: