所属类别:技术博客
文章作者:wanginvc
特别推荐:免费发布信息 承包关键词~~抢爆了!HOT!
技术简介 JNI,全称javanativeinterface,是java语言与其他类型语言交互的接口。 图1是Sun站点上对JNI的基本结构的描述。 技术应用 Java程序调用其他语言编写的程序。 其他语言创建Java虚拟机调用Java程序。技术细节Java中调用C/C++方法:1、NativeMethodDeclaration (原生函数声明) 原生函数声明是Java虚拟机与原生函数之间的桥梁。同样可以这样理解,它是Java虚拟机与本地库文件之间的接口。在具体运作上,它与普通的Java函数声明没有大的区别,除了要在函数声明前添加native关键字。函数声明的参数表应为Java的内部类型,有相应的JNI函数完成Java与其他不同语言之间的数据类型转换。 示例: nativebyte[]loadFile(Stringname);2、LoadtheLibrary (装载原生函数库) 原生函数库文件包含了原生函数的代码实现,通过调用System.loadLibrary进行装载.通常把它放置在静态初始化块中,以保证每个类只初始化一次,当然这也可以在别的地方.需要注意的是,你需要配置系统的环境以保证在运行时可以找到要装载的库文件. 示例: static{ System.loadLibrary("nativelib"); }3、CompliletheProgram(编译程序) 调用javac编译java源文件. 示例: javactest.java4、GeneratetheHeaderFile 调用javah生成C/C++需要的头文件.生成头文件主要内容为C/C++语言的原生函数声明,它是Java语言与C/C++语言之间的接口.生成的原生函数声明来自Java中的原生函数声明,但有所不同.新的函数声明函数名字以及参数名字,均依照jni内部规则生成.需要注意的是:参数表的第一个总会是JNIEnv*,虚拟机环境信息指针;第二个参数依据Java中原生函数声明不同而不同,静态函数为jclass,非静态函数为jobject. 示例: javahtest5、ImplementtheNativeMethod(实现原生方法) 包含生成的原生函数头文件和jni.h,编写原生函数的实现.原生函数内容主要为C/C++语言编写的平台相关代码,同时涉及与Java虚拟机之间数据的交换.JNI提供了相应的C/C++语言实现的转换函数,通过传入JNIEnv*env给转换函数可以完成数据转换.详情见jni.h6、CompiletheDynamicorSharedObjectLibrary 编译C/C++程序,生成动态链接库,具体操作如下图所示7、RuntheExample 执行Java程序.需要注意,需要保证编译产生的动态链接库能够被正确装载.下图是不同操作系统下,指定当前目录为库文件目录的示例.如果指定为你它路径可仿此例.C/C++调用Java 注意:下述示例代码基本为C语言形式,可以采取C++语言形式.建议这样做,因为可以显著减少JNIEnv*env的书写次数.如env->GetMethodID(cls,"test","(I)I")对比(*env)->GetMethodID(env,cls,"test","(I)I")显然,C++的书写更加简洁方便.1、初始化虚拟机 本地代码在调用Java方法之前必须先加载Java虚拟机,而后所有的Java程序都在虚拟机中执行。为了初始化Java虚拟机,JNI提供了一系列的接口函数InvocationAPI。通过这些API可以很方便地将虚拟机加载到内存中。创建虚拟机可以用函数 jintJNI_CreateJavaVM(JavaVM**pvm,void**penv,void*args)。对于这个函数有一点需要注意的是,在JDK1.1中第三个参数总是指向一个结构JDK1_1InitArgs, 这个结构无法完全在所有版本的虚拟机中进行无缝移植。在JDK1.2中已经使用了一个标准的初始化结构JavaVMInitArgs来替代JDK1_1InitArgs。下面我们分别给出两种不同版本的示例代码JDK1.1初始化虚拟机JDK1.2初始化虚拟机 通常我们采取JDK1.2的做法options中的options[2]可以不提供,仅仅为了调试.JNI_CreateJavaVM函数的第二个参数JNIEnv*env,就是贯穿整个JNI始末的一个参数,因为几乎所有的函数都要求一个参数就是JNIEnv*env。2、类定义的获取 已知类的完整路径名,调用FindClass方法获取jclass类定义; 已知类的某个对象,调用GetObjectClass获取对象对应的jclass类定义3、类实例对象的创建 调用AllocObject产生jclass类定义对应的类实例对象;这不会调用任何类的构造函数,仅仅是内存创建。 调用NewObject产生jclass类定义对应的类实例对象;这需要提供类的构造方法的MethodID,会调用指定的MethodID对应的构造函数。(MethodID的获取见下文类方法的获取)4、类方法的调用方法一:静态方法的调用 调用GetStaticMethodID获取jclass类定义指定类静态方法的MethodID;需要提供静态方法的名字和参数表信息。这可以通过jdk内置的命令 javap-s-c类名字(不含class扩展名)得到。 调用CallStaticMethod执行指定MethodID对应的类的静态方法,其中应替换为函数返回值类型的描述.如CallStaticIntMethod完成调用所有的返回值为Int的类的静态方法.方法二:非静态方法的调用 调用GetMethodID获取jclass类定义指定类的非静态方法的MethodID;需要提供非静态方法的名字和参数表信息。这可以通过jdk内置的命令 javap-s-c类名字(不含class扩展名)得到。 调用CallMethod执行指定MethodID对应的类的非静态方法,其中应替换为函数返回值类型的描述.如CallStaticIntMethod完成调用所有的返回值为Int的类的非静态方法. 注意:构造函数作为特例,方法名字为"".5、类内部变量的读取与设置方法一:静态成员变量的读取与设置 调用GetStaticFieldID获取jclass类定义指定类的静态成员变量的FieldID;需要提供静态成员变量的名字和变量类型信息。这可以通过jdk内置的命令 javap-s-c类名字(不含class扩展名)得到。 调用GetStaticField 读取指定FieldID对应的类的静态成员变量,其中应替换为变量类型的描述,需要提供类定义jclass.如GetStaticIntField完成读取所有的变量类型为Int的类的静态成员变量. 调用SetStaticField 设置指定FieldID对应的类的静态成员变量,其中应替换为变量类型的描述,需要提供类定义jclass.如SetStaticIntField完成设置所有的变量类型为Int的类的静态成员变量.方法二:非静态成员变量的读取与设置 调用GetFieldID获取jclass类定义指定类的非静态成员变量的FieldID;需要提供非静态成员变量的名字和变量类型信息。这可以通过jdk内置的命令 javap-s-c类名字(不含class扩展名)得到。 调用GetField 读取指定FieldID对应的类对象jobject的非静态成员变量,其中应替换为变量类型的描述,需要提供类对象jobject.如GetIntField完成读取所有的变量类型为Int的类对象的非静态成员变量. 调用SetField 设置指定FieldID对应的类对象的非静态成员变量,其中应替换为变量类型的描述,需要提供类对象jobject.如SetIntField完成设置所有的变量类型为Int的类对象的非静态成员变量.6、数组的创建与访问 数组是Java内部数据的集合形态,通过JNI提供的数组相关操作,可以获取和设置内部数据。它通常是Java代码与本地代码数据交换的纽带,如Java原生方法声明test(int[]arg),对应的形参类型为jintArray;在对应的本地方法中,通过数组的相关方法可以更改arg数组的内容,达到交互的目的。当然,调用方法更改数组内容方式不同,效果也不同。可以产生值传递(产生数组备份,不更改数组实际内容)和址传递(不产生数组备份,直接更改数组实际内容)两种效果. 调用NewArray产生指定类型的数组,其中为数组内部包含元素的类型. 调用GetArrayElements获取指定jArray数组的数组头指针,其中为数组内部包含元素的类型,为主类型,不包括Object类元素。jboolean*isCopy参数可以指定是否拷贝数组元素,返回拷贝数组的头指针. 调用ReleaseArrayElements通知jvm释放GetArrayElements获得的数组头指针.依据提供的mode参数,可以指定对数组的更改是否应用到原始jarray. 0拷贝更改内容到原数组并且释放获取的头指针 JNI_COMMIT拷贝更改内容到原数组但不释放获取的头指针 JNI_ABORT 释放获取的头指针但不拷贝更改内容到原数组 调用GetArrayRegion获取指定jArray数组的元素子集的拷贝,存放到指定的主类型数组指针中,其中为数组内部包含元素的类型,为主类型,不包括Object类元素.Object类元素只能获取一个指定的Object元素,GetObjectArrayElement. 调用SetArrayRegion设置指定jArray数组的元素子集,数据来源为提供的主类型数组,其中为数组内部包含元素的类型,为主类型,不包括Object类元素.Object类元素只能设置一个指定的Object元素,SetObjectArrayElement.7、Java内置的String的创建与转换创建String 调用NewString根据unicode字符串创建对应的String 调用NewStringUTF根据标准UTF-8字符串创建对应的String获取String长度 调用GetStringLength获取String占用的unicode字符串长度 调用GetStringUTFChars获取String占用的UTF-8字符串长度获取String内容 调用GetStringChars获取String内容对应的unicode字符串,根据isCopy参数可以指定,是否拷贝String内容作为unicode字符串. 调用GetStringUTFChars获取String内容对应的标准UTF-8字符串,根据isCopy参数可以指定,是否拷贝String内容作为UTF-8字符串.释放或提交获取的String内容 调用ReleaseStringChars释放获取的unicode字符串. 调用ReleaseStringUTFChars释放获取的UTF-8字符串.8、异常捕捉与处理抛出异常 调用Throw抛出给定的异常对象jthrowable 调用ThrowNew抛出给定异常子类对应的异常对象 调用FatalError抛出致命异常信息,jvm不能捕捉捕获异常 调用ExceptionOccurred捕获当前jvm中的异常,并返回 调用ExceptionDescribe打印当前异常信息。清除异常 调用ExceptionClear清除当前抛出的异常。9、多线程处理 有些时候需要使用多线程的方式来访问Java的方法。这里面涉及到两个概念,它们分别是虚拟机(JavaVM*jvm)和虚拟机环境(JNIEnv*env)。真正消耗大量系统资源的是jvm而不是env,jvm是允许多个线程访问的,但是env只能被创建它本身的线程所访问,而且每个线程必须创建 自己的虚拟机环境env。 调用AttachCurrentThread将当前线程连接到指定的JavaVM*jvm,并创建虚拟机环境JNIEnv*env;调用DetachCurrentThread断开与指定的JavaVM*jvm的连接,并且自动释放分配的虚拟机环境JNIEnv*env.10、全局与局部引用 在C/C++本地语言中产生的java对象jobject,在函数中创建,为函数内部的局部引用。当函数退出,jvm会回收相应分配的资源。如果对象要持久存在,则应该为jobject创建全局引用,这样垃圾收集器不会进行收集,直到调用jni方法显示删除。创建引用 调用NewGlobalRef产生对象引用对应的全局引用,原对象引用可以为局部或全局。删除引用 调用DeleteGlobalRef删除指定的全局引用. 调用DeleteLocalRef删除指定的局部引用参考文档: j2se1_42_jni.chmJDK1.42的jni规范
发表于 @ 2006年09月19日 10:50:00评论(loading...AddFeedbackCountStack("1244216"))编辑新一篇:MFC CAsyncSocket & CSocket失败之处
相关信息· 不错的主要用于加密的vbs(asp)位移运算类
· 方法及方法重载
· 在Linux下使用DOS/Windows磁盘
· JSP安全编程实例浅析
74273
46637
