




已阅读5页,还剩20页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
jni学习笔记开篇java是跨平台的语言,但是在有些时候仍需调用本地代码(通常由c、c+开发)sun公司提供的jni是java平台的一个功能强大的接口,这个jni接口提供了java与操作系统本地代码相互调用的功能。最简单的java调用c、c+代码的步骤helloworld首先在java类中声明一个native方法package .higinet.jni;public class helloworld public void sayhello();/ 本地方法声明。public st native atic void main(string args) 使用javah命令生成包含native方法定义的c/c+头文件jni .higinet.jni.helloworld:这条命令会生成一个.h的头文件:cn_com_higinet_jni_helloworld.h/* do not edit this file - it is machine generated */#include /* header for class cn_com_higinet_jni_helloworld */#ifndef _included_cn_com_higinet_jni_helloworld#define _included_cn_com_higinet_jni_helloworld#ifdef _cplusplusextern c #endif/* * class: cn_com_higinet_jni_helloworld * method: sayhello * signature: ()v */jniexport void jnicall java_cn_com_higinet_jni_helloworld_sayhello (jnienv *, jobject);#ifdef _cplusplus#endif#endif按照生成的c/c+头文件来写c/c+源文件【1】打开visual studio 2005,新建一个项目:选择visual c+ win32win32控制台应用程序输入项目名javanative确定然后选择dll空项目完成。【2】将之前生成好的.h头文件拷到javanative的c+项目目录之下并将之引入到解决方案的头文件目录下,然后在源文件中新建c+源文件,写具体的实现#include cn_com_higinet_jni_helloworld.h;#include using namespace std;jniexport void jnicall java_cn_com_higinet_jni_helloworld_sayhello (jnienv * evn , jobject obj)couthello world!endl;【3】编译之,出现如下错误: fatal error c1083: 无法打开包括文件:“jni.h”: no such file or directory这是因为在cn_com_higinet_jni_helloworld.h这个头文件中用到了jni.h这个头文件,而这个头文件在javahome/ include文件夹下面,所以将之拷贝到c+项目的目录下,但是在cn_com_higinet_jni_helloworld.h这里面是#include,这种方式是在系统目录下找的,因为也需要将之改为#include”jni.h”,这样就在项目目录下找了。 fatal error c1083: 无法打开包括文件:“jni_md.h”: no such file or directory这是因为在系统目录下也没有jni_md.h这个头文件中,可以将javahome/include/win32下找到拷贝到项目目录下。这样编译通过将c/c+源文件译成动态连接库(dll)点击工具栏上的生成,生成javanative,这样在debug目录下就会生成一个javanative.dll文件将dll文件加入到path环境变量下。将之前生成的dll文件的目录加入的path环境变量之下java类中加载dll,然后调用声明的native方法package .higinet.jni;public class helloworld public native void sayhello();/ 本地方法声明。public static void main(string args) system.loadlibrary(javanative);/ exception in thread main java.lang.unsatisfiedlinkerror: no/ javanative in java.library.path/ eclipse启动的时候会读取一次环境变量,之后就不再读取了,因此应该重启eclipsenew helloworld().sayhello();最终打印出了: hello world!使用jni的两个弊端使用了jni,那么java application将不能跨平台了,如果要移植到别的平台上,那么native代码就要重新进行编译java是强类型的语言,而c/c+不是因此写jni代码时更为小心总之,必须在构建java程序的时候,尽量少用本地代码。本地代码访问java代码在被调用的c/c+函数中也可以反过来访问java程序中的类javah工具生成的c/c+函数声明中,可以看到有两参数:jniexport void jnicall java_cn_com_higinet_jni_helloworld_sayhello (jnienv *, jobject);jnienv类型实际上代表了java环境,通过这个jnienv *指针就可以对java端的代码进行操作,例如:创建java类的对象,调用java对象的方法,获取java对象的属性等等,jnienv的指针会被jni传入到本地方法的实现函数中来对java端的代码进行操作jnienv类中有很多函数:newobject/newstring/newarrayget/setfieldget/setstaticfieldcallmethod/callstaticmethod对于第二个参数obj,如果是非静态的方法,当调用这样的方法时,与这个方法相关联的对象的引用将传入到c/c+方法中的obj上,对于静态方法,obj指的代表那个类的class对象java类型在c/c+中的映射关系jclass的取得 为了能够在c/c+中使用java类,jni.h头文件中专门定义了jclass类型来表示java中的class类。typedef _jclass *jclass; jnienv类中有如下几个简单的函数可以取得jclass:jclass findclass(const char *name) jclass getobjectclass(jobject obj) jclass getsuperclass(jclass sub) findclass会在classpath系统环境变量下寻找类传入完整类名,注意包与包之间是用”/”而不再是”.”来分隔,如:jclass cls_string = envfindclass(“java/lang/string”);访问java类中的属性和方法 在c/c+本地代码访问java端的代码,一个常见的问题就是获取类的属性和调用类的方法,为了在c/c+中表示属性和方法,jni在jni.h头文件中定义了jfieldid,jmethodid类型分别代表java端的属性和方法。 我们在访问或是设置java属性的时候,首先就要在本地代码获取代表该java类属性的jfieldid ,然后才能在本地进行java属性操作,同样的,我们需要呼叫java端的方法时,也是需要取得该方法jmethodid才能进行java方法调用 使用jnienv的n getfieldid/getmethodidn getstaticfieldid/getstaticmethodid来取得相应的jfieldid和jmethodid getfieldid(jclass clazz,const char*name,const char*sign); getstaticfieldid(jclass clazz,const char*name,const char*sign); getmethod id(jclass clazz,const char*name,const char*sign); getstaticmethodid(jclass clazz,const char*name,const char*sign); getmethodid也能取得构造函数的methodid,创建一个java对象时可以调用指定的构造方法,如:envgetmethodid(data_class,”,”()v”); 类似java的reflect需要指定类跟属性/方法名来取得相应的jfieldid跟jmethodid,而sign又是什么呢?sign是什么?例如testnative类中有两个重载的方法:public class testnativepublic void function(int i)system.out.println(“ integer:“+i);public void function(double j)system.out.println(“double:”+j);/要取得其中的一个方法,我们首先要取得这个方法的类jclass clazz_testnative = envfindclass(“testnative”);/类的完整类名/取得jmethodid之后才能调用jmethod id_fun = envgetmethodid(class_testnative,”function”,”?”);但是这样取得的是哪一个呢?因为有重载,所以确定不了,这就需要sign来指定类型了。即:sign如果指定为”(i)v”;则取回void function(int)的jmethodid如果指定为”(d)v”则取回void function(double)的jmethodidsign签名用来表示要取得的属性/方法的类型使用签名取得属性/方法id的例子import java.util.date;public class hello public int property;public int function(int foo, date date, int arr) system.out.println(funtion);return 0;public native void test();/test本地方法的实现jniexport void jnicall java_hello_test (jnienv * evn , jobject obj)/因为test不是静态函数,所以传进来的就是调用这个类的函数的对象/否则就传入一个jclass对象表示native方法所在的类jclass hello_clazz = env-getobjectclass(obj);jfieldid fieldid_prop = env-getfieldid(hello_clazz,property,i);jmethodid methodid_func = env-getmethodid(hello_clazz,function,(iljava/util/date;i)i );env-callintmethod(obj,methodid_func,0l,null,null);/invoke!解说:取得的property是int类型的,所以在签名中传入i取得function的id时由于这样的签名是难以记忆的,所以:使用javap命令行来签名jdk也提供了一个工具javap来查看一个类的声明,其中就可以设置输出的每个方法/属性的签名比如使用:javap -s -public .higinet.jni.helloworld命令后:取得java属性/设计java属性值取得了相应属性的jfield之后就可以用set field,get field,setstaticfield跟getstaticfield等函数对java属性进行操作了。这些函数在jni.h中都有定义,如:jint getintfield(jobject obj, jfieldid fieldid) return functions-getintfield(this,obj,fieldid);怎样获取数组属性?可以使用getobjectfield来取得数组类型的属性。下面写一个例子,在java文件中定义一个变量,然后在c中修改它,打印出来。public class helloworld /以下定义了一个int类型的变量number,我们要在c中修改它,然后打印。public int number = 10 ;在c中:#include cn_com_higinet_jni_helloworld.h;#include using namespace std;jniexport void jnicall java_cn_com_higinet_jni_helloworld_sayhello (jnienv * env , jobject obj)jclass helloworld_clazz = env-getobjectclass(obj);jfieldid number_fieldid = env-getfieldid(helloworld_clazz,number,i);jint number = env-getintfield(obj,number_fieldid);cout number setintfield(obj,number_fieldid,100l);/注意,java中的int对应本地的long类型,所以要在后面加上l然后在java中打出来:hw.sayhello();system.out.println(hw.number);java方法的调用jnienv提供了众多的callmethod跟callstaticmethod还有callnonvirtualmethod函数,需要通过getmethodid取得相应的方法的jmethodid来传入到上述函数中。调用实例方法的三种形式:1、常用方式:callmethod(jobject obj,jmethodid id,)2、当调用这个的时候有一个指向参数表的va_list变量时使用callmethodv(jobject obj,jmethodid id,va_list lst );3、当调用这个函数时候有一个指向jvalue或jvalue数组的指针时使用callmethoda(jobject obj,jmethodid id,jvalue* v);如:在java中的一个方法:public boolean function(int i,double d,char c)则在c中如下调用:env-callbooleanmethod(obj,id_function,100l,3.44,la);解释:因为java中的int对应本地的long所以100后面加一个l,因为java中的char是unicode,是宽字符,所以要la这样写。调用静态方法的三种形式:callstaticmethod(jclass clazz,jmethodid id,);calstaticmethodv(jclass clazz,jmethodid id,va_list lst);callstaticmethoda(jclass clazz, jmethodid id,jvalue * vars);现在写一个例子,用来展示在c中调用java函数:首先在java中写一个这样的函数:/返回两者的最在值。public double max(double num1,double num2)return num1 num2?num1:num2;然后在c中写:jclass helloworld_clazz = env-getobjectclass(obj);jmethodid id_max = env-getmethodid(helloworld_clazz,max,(dd)d);jdouble d = env-calldoublemethod(obj,id_max,20.2,30.2);coutdendl;然后在java中运行即可打印最大的值。callnonvirtual method有如下java代码:public class fatherpublic void function()system.out.println(“father”);public class child extends fatherpublic void function()system.out.println(“child”);则father p = new child();p.function();肯定会调用子类的方法而无法调用父类的方法。但是在c+中是可以的。如下c+代码:class fatherpublic:void function()cout”father”endl;class child:public fatherpublic :void function()cout”father”endl;father * p = new child();pfunction();上面的代码会调用父类的方法。如果改为如下的:class fatherpublic:virtual void function()cout”father”endl;class child:public fatherpublic :void function()cout”father”endl;father * p = new child();pfunction();则会调用子类的方法。在jni中定义的callnonvirtualmethod就能够实现子类对象调用父类方法的功能,如果想要调用一个对象的父类的方法而不是子类的这个方法的话,就可以使用callnonvirtualmethod.要使用它,首先要取得父类及要调用的父类的jmethodid,然后传入到这个函数就能通过子类对象呼叫被覆盖(override)的父类的方法。在java文件中:public class father public void function()system.out.println(father.);public class child extends father overridepublic void function() system.out.println(child.);public class helloworld public native void sayhello();/ 本地方法声明。/试图用子类的实例去执行父类被覆盖的方法father p = new child();那么在本地方法中:jclass helloworld_clazz = env-getobjectclass(obj);jfieldid id_p = env-getfieldid(helloworld_clazz,p,lcn/com/higinet/jni/father;);jobject p = env-getobjectfield(obj,id_p);jclass clazz_father = env-findclass(cn/com/higinet/jni/father);jmethodid id_father_function = env-getmethodid(clazz_father,function,()v);env-callvoidmethod(p,id_father_function);这样将调用java中子类child的函数function,那么如何调用父类的function方法呢?如果改为:env-callnonvirtualvoidmethod(p,clazz_father,id_father_function);将会调用父类中的方法。 在c/c+本地代码中创建 java对象 在c/c+本地代码中访问java的string字符串对象 在c/c+本地代码中创建java的string字符串对象java对象的创建newobject 使用函数newobject可以用来创建java对象 getmethodid能够取得构造方法的jmethodid,如果传入的要取得方法的名称设定为”就能取得构造方法 构造方法的返回值类型为voidjobject newobject(jclass clazz, jmethodid methodid, .)例子:jclass clazz_date = env-findclass(“java/util/date”);jmethodid mid_date = getmethodid(clazz_date,”,”()v”);jobject now = env-newobject(clazz_date,mid_date);例子代码如下:1、 写如下java代码:package .higinet.jni;import java.util.date;/* * 在本地实现java类的实例化 */public class javanative2 public static native void outputdate();public static void main(string args) 2、之后由javah命令生成头文件:javah .higinet.jni.javanative23、生成头文件后新建c+项目:javanative,将头文件导入进来并进行源码编写#include cn_com_higinet_jni_javanative2.h#include using namespace std;jniexport void jnicall java_cn_com_higinet_jni_javanative2_outputdate(jnienv *env, jclass jclazz)jclass clazz_date = env-findclass(java/util/date);jmethodid mid_date = env-getmethodid(clazz_date,()v);jobject now = env-newobject(clazz_date,mid_date);jmethodid time_date = env-getmethodid(clazz_date,gettime,()j);/得到gettime()方法jlong t = env-calllongmethod(now,time_date);cout t findclass(“java/lang/string”);jmethodid methodid_str = env-getmethodid(class_str,”,”c)v”);/预先创建一个没有初始化的字符串jobject string = env-allocobject(clazz_str);/创建一个4个元素的字符串数组,然后以清 原 卓 也 赋值jchararray arg = env-newchararray(4);env-setchararrayregion(arg,0,4,l”清原卓也”);/呼叫构造方法env-callnonvirtualvoidmethod(string,clazz_str,methodid_str ,str);jc lass clazz_this = env-getobjectclass(obj);/这里假设这个对象的类中没有定义static string static_strjfieldid fieldid_str = env-getstaticfieldid(clazz_this,”static_str”,”l/java/lang/string”);env-setstaticobjectfield(clazz_str,fieldid_str,string);java字符串c/c+的字符串在java中,使用的字符串string对象是unicode(utf-16)码,即每个字符不论是中文还是英文还是符号,一个字符总是占两个字节java通过jni接口可以将java的字符串转换到c/c+中的宽字体串(wchar_t*),或是传回到一个utf-8的字符串(char*)到c/c+ ,反过来c/c+可以通过一个宽字符串或是一个utf-8编码的字符串创建一个java端的string对象。相关函数介绍:getstringcharsgetstringutfchars这两个函数用来取得某个jstring对象相关的java字符串,分别可以取得utf-16编码的宽字符串(jchar*)跟utf8编码的字符串(char*)const jchar* getstringchars(jstring str,jbolean * copied)const char* getstringutfchars(jstring str,jboolean * copied)第一个参数传入一个指向java中的string对象的jstring变量第二个参数传入的是一个jboolean的指针这两个函数分别都会有两个不同的动作1、 开辟新内存,然后把java中的string拷贝到这个内存中,然后返回一个指向这个内存的指针2、 直接返回指向java中string的内存的指针,这个时候千万不要改变这个内存的内容,这将破坏string在java中始终不变这个原则第二个参数是用来标识是否对java的string对象进行了拷贝的如果传入的这个jboolean指针不是null,则他会给该指针所指向的内存传入jni_ture或jni_false标识是否进行了拷贝,转入null表示不关心是否拷贝字符串,它就不会给jboolean*指向的内存赋值使用这两个函数取得的字符串,在不使用的时候,要使用releasestringchars/releasestringutfchars来释放拷贝的内存,或是释放对java的string对象的引用releasestringchars(jstring jstr,const jchar* str)releasestrngutfchars(jstring jstr,const char*str)第一个参数指向一个jstring变量,即是要释放的本地字符串的来源第二个参数就是要释放的本地字符串getstringcritical为了增加直接返回指向java字符串的指针的可能性(而不是拷贝),jdk1.2有了新的函数getstringcritical/releasestringcritical定义为:const jchar* getstringcritical(jstring str,jboolean* copied)void releasestringcritical(jstring jstr,const jchar*str)在getstringcritical/releasestringcritical之间是一个关键区域,在这个关键区域中不能呼叫jni的其他函数和会造成当前线程中断或是会让当前线程等待的任何本地代码,否则会造成关键区代码执行期间垃圾回收器停止运作,任何触发垃圾回收器的线程也会暂停,其他的触发垃圾回收器的线程不能前进直到当前线程结束而激活垃圾回收器在关键区域中千万不要出现中断操作,或是在jvm中分配任何新的对象,否则会造成jvm死锁虽说这个函数会增加直接传回指向java字符串的指针的可能性,不过还是会根据情况传回拷贝过的字符串不支持getstringutfcritical,没有这个函数,由于java字符串是utf16要转成utf8编码始终要进行一次拷贝,所以没有这样的函数。getstringregiongetstringutfregionjava1.2出来的函数,这个函数的动作,是把java字符串的内容直接拷贝到c/c+的字符串数组中,在呼叫这个函数之前必须有一个c/c+分配出来的字符串,然后传入到这个函数中进行字符串的拷贝由于c/c+中分配的内存开销相对小,而且java中的string内容拷贝的开销可以忽略,更好的一点是此函数不分配内存,不会招聘outofmemoryerror异常拷贝java字符串并以utf-8传入buffergetstringutfregion(jstring str,jsize start,jsize len,char * buffer);拷贝java字符串并以utf16编码传入buffergetstringregion(jstring str,jsize start,jsize len,jchar* buffer);其他的字符串函数:jstring newstring(const jchar* str,jsize len)jstring newstringutf(const char* str)jsize getstringlength(jstrng str)jsize getstringutflength(jstring str)实例:1、 在java中申明一个字符串变量message和输出函数如下:package .higinet.jni;import java.io.bufferedreader;import java.io.ioexception;import java.io.inputstreamreader;/* * java与c/c+字符串的转换 * author xuyanhua */public class jnstring public string message ;public native void outputmsg();public static void main(string args) throws ioexception 2、 写如下代码在c程序中:include cn_com_higinet_jni_jnstring.h#include windows.h#include using namespace std;jniexport void jnicall java_cn_com_higinet_jni_jnstring_outputmsg (jnienv *env, jobject obj)jfieldid fid_msg = env-getfieldid(env-getobjectclass(obj),message,ljava/lang/string;);/得到jfieldidjstring j_msg = (jstring)env-getobjectfield(obj,fid_msg);/const jchar* jstr = env-getstringchars(j_msg,null);messageboxw(null,(const wchar_t*)jstr,ltitle,mb_ok);env-releasestringchars(j_msg,jstr);上面代码的含义是将取得的字符串转换为c中的宽字符串,然后显示到一个对话框上。3、然后在java中调用:system.loadlibrary(jnstring);bufferedreader reader = new bufferedreader(new inputstreamreader(system.in);string str = reader.readline();jnstring jns = new jnstring();jns.message = str ;jns.outputmsg();运行程序,输入任意字符串,会显示:将字符串倒序排列:#include cn_com_higinet_jni_jnstring.h#include windows.h#include #include #include using namespace std;jniexport void jnicall java_cn_com_higinet_jni_jnstring_outputmsg (jnienv *env, jobject obj)jfieldid fid_msg = env-getfieldid(env-getobjectclass(obj),message,ljava/lang/string;);/得到jfieldidjstring j_msg = (jstring)env-getobjectfield(obj,fid_msg);/const jchar* jstr = env-getstringchars(j_msg,null);/messageboxw(null,(const wchar_t*)jstr,ltitle,mb_ok);wstring wstr(const wchar_t*)jstr);env-releasestringchars(j_msg,jstr);/释放std:reverse(wstr.begin(),wstr.end();jstring j_new_str = env-newstring(const jchar*)wstr.c_str(),wstr.size();env-setobjectfield(obj,fid_msg,j_new_str);在java代码中输出即可:system.out.println(jns.message);/输出倒序后的字符串如上所述,我们也可以使用getstringcritical,它的主要做用是增加返回字符串本身指针的可能性。对于上面的代码我们只要将其中的getstringchars变为getstringcritical即可,参数不用变除此之外,我们还可以用getstringregion#include cn_com_higinet_jni_jnstring.h#include windows.h#include #include #include using namespace std;jniexport void jnicall java_cn_com_higinet_jni_jnstring_outputmsg (jnienv *env, jobject obj)jfieldid fid_msg = env-getfieldid(env-getobjectclass(obj),message,ljava/lang/string;);/得到jfieldidjstring j_msg = (jstring)env-getobjectfield(obj,fid_msg);/动态生成字符串数组,需要知道其长度jsize len = env-getstringlength(j_msg);jchar* jstr = new jcharlen+1;env-getstringregion(j_msg,0,len,jstr);/将java字符串中的数据拷贝到本地的内存中jstrlen=0;/此处防止乱码wstring wstr(const wchar_t*)jstr);delete jstr;std:reverse(wstr.begin(),wstr.end();jstring j_new_str = env-newstring(const jchar*)wstr.c_str(),(jint)wstr.size();env-setobjectfield(obj,fid_msg,j_new_str);处理数组数组分为两种:1、 基本类型的数组2、 对象类型(object)的数组一个能通用于两种不同类型数组的函数getarraylength(jarray array)处理数组基本类型数组处理基本数组的时候也是跟处理字符串类似,有很相似的函数getarrayelements(array arr,jboolean* iscopied);这类函数可以把java基本类型的数组转换到c/c+中的数组,有两种处理方式,一是拷贝一份传回本地代码,另一个是把指向java数组的指针直接传回到本地代码,处理完本地化的数组后,通过releasearrayelements类释放数组releasearrayelements(array arr,*array,jint mode)用这个函数可以选择将处理java跟c+的数组,是提交还是撤销等,内存释放还是不释放等mode可以取下面的值:0 对java的数组进行更新并释放c/c+的数组jni_commit对java的数组进行更新但不释放c/c+的数组jni_abort对java的数组不进行更新,释放c/c+的数组getprimitivearraycritical(jarray arr,jboolean* iscopied);releaseprimitivearraycritical(jarray arr,void*array ,jint mode);也是jdk1.2出来的,为了增加直接传回指向java数组的指针而加入的函数,同样的,也会有同getstringcritical的死锁问题。getarrayregion(array arr,jsize start,jsize len,*buffer);在c/c+预先开辟一段内存,然后把java基本类型的数组拷
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 丽清电子面试题库及答案
- 兰州文员面试题库及答案
- 安全教育培训资料记录课件
- 康田物业面试题库及答案
- 农业产业园项目2025年农业废弃物资源化利用效益评估报告
- 安全教育培训评价语课件
- 2025年健康养生食品行业产品质量安全监管报告
- 关于2025年文化旅游示范区建设资金申请的旅游产业可持续发展研究报告
- 2025年有声读物版权纠纷案例解析与法律风险防范研究报告
- 江西省赣州市南康中学2025-2026学年高二上学期开学考试物理试题(含答案)
- 校本课程篆刻教学设计
- GB/T 20967-2007无损检测目视检测总则
- GB/T 12220-2015工业阀门标志
- 当代世界经济与政治第二章课件
- PS考试试题及答案
- 新都区文化产业发展建议报告
- 时代邻里4度°服务美学品质关怀体系
- 养老机构行政值班查房记录表格
- EPC合同条件(银皮书)-1999
- 外研版五年级上册英语(全册)单元教材分析
- 华为-计划、预算和核算
评论
0/150
提交评论