打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
用Jace整合Java和C++
userphoto

2012.08.08

关注
2007-01-01 23:26:30  来源:yesky  作者:刘彦青编译  编辑:刘彦青编译
摘要
Jace是一种免费的开放源代码的工具,它使我们能够轻松地开发JNI(Java本机接口)代码。本篇文章详细地分析了JNIAPI的问题,以及如何使用Jace解决这些问题。
如果没有更深的了解,我们一定会以为Sun设计JNI的目的是为了不让Java编程人员使用它。毕竟,类型安全形同虚设,缺乏错误检查机制,进行一次简单的Java方法调用需要4次或更多的JNI调用,这都是JNI明显的不足之处。另外,我们还必须管理JNIEnv指针,不能在多个线程中使用JNI调用,必须为每种可能的操作在9个函数调用中进行选择,而且异常信息的获取也非常地困难。这还只是JNI所出现问题的一部分,我们还能发现许多其他问题。
这些限制中的许多部份都与JNI与C语言的绑定有关,C语言本身对类型安全、异常处理机制的支持也非常不好。尽管目前大多数的编程人员都已经能够使用C++编写代码,但Sun没有放弃C编程人员,这也是JNI目前这种状况的原因。不幸的是,这种很难使用的API给开发人员带来了许多困难。
Jace是一款免费的开放源代码的工具包,旨在使JNI编程变得更加简单。它支持由Java类文件自动生成C++代理类以及C++与Java的异常、数组、包、对象的整合,管理Java引用的线程绑定和生命周期。更为重要的是,它能够使我们开发更小、更易于理解、在编译时类型安全的模块。
JNI的类型系统
Jace最基本的特点是它使用C++代理类来表达Java类型。为了真正地理解代理类的优点,我们首先需要来看看JNI的类型系统。Sun在JNI中使用了24种C类型来表示所有可能的Java类型。JNI包含有9个简单类型:
·jboolean
·jbyte
·jchar
·jshort
·jint
·jlong
·jdouble
·jfloat
·void
JNI有14种引用类型,如下图所示:
(图:picture01)
另外,JNI有一个复合型的类型jvalue,它能够表达所有的简单和引用类型。
Jace类型系统
图2表示基本的Jace数据类型的类图表。这些类是我们访问Jace运行时间库的简单的接口,它与JNI的数据类型对应非常紧密。
(图:picture02)
Jace的数据类型系统是直接以24种JNI数据类型为基础的,对于每一种JNI数据类型而言,Jace都有一个相应的C++代理类。9种JNI简单数据类型以及jvalue、jclass、jobject、jstring和jthrowable都直接映射为相应的Jace代理类,JNI的jarray数据类型以及9个派生的数组数据类型都被映射为一种基于模板的JArray数据类型。在下面的部分中,我们将对每种C++代理类进行详细的解释。
简单类
9个简单的类可以作为9种JNI的简单数据类型的封装器。我们可以将这些类作为参数,并返回其他C++代理类的值:
/* 获得值为“A String”的java.lang.String的哈希码值
*/
JInt hashCode = String( "A String" ).hashCode();
我们也可以将这些类作为JArray类的模板参数:
/* 创建一个大小为512的字节缓冲区
*/
JArray<JByte> buffer( 512 );
JValue
JValue是所有代理类的基础类,它能够表达Java所有的简单和引用数据类型。每个JValue有一个JClass,该JClass表示jvalue相应的jclass。我们只能提供一个JNI的jvalue数据类型构建JValue,JValue就成为了jvalue的持有者。大多数开发人员无需与JValues直接打交道。
JClass
JClass表示JNI的数据类型jclass,它提供了访问其jclass和在不同的JNI调用中表示jclass的字符串(例如,java/lang/Object和Ljava/lang/Object)。Jace的框架使用JClass实例提供进行GetMethodID()、GetFieldID()和NewObjectArray()等JNI调用所必需的信息。大多数开发人员无需直接与JClass打交道。
JObject
JObject类表示JNI的数据类型jobject,并作为所有引用数据类型的基础类。除了最重要的JValue::getJavaValue()外,JObject类还提供了getJavaObject()方法。除了getJavaObject()能够解开jvalue,并将它放在jobject中外,这二个方法的功能相当。
JObject比较有趣,因为Java的引用类型有一些Java的简单数据类型所不具备的特性:
·引用类型没有自己的值,它们只是指向这些值。我们可以用二种方式构建JObject子类。第一种方式,我们可以将它构建为现有Java对象的引用。通过使用接受jobject(或包含jobject的jvalue)为参数的构建器或者使用C++的拷贝构建器,我们就可以以这种方式构建JObject子类。
在对JObject子类实例化时,子类实例将它自己提交给作为参数提供的jobject引用。(实例使用NewGlobalRef()创建jobject的全局性引用。)
using jace::java::net::URL;
JNIEXPORT void JNICALL Java_foo_Bar_someMethod( JNIEnv *env,jobject jURL ) {
/* 创建jURL的一个引用,而不是一个新的URL
*/
URL url( jurl );
/* 既然已经实例化了C++代理对象,我们就就可以方便地对jURL调用toString()等方法
*/
std::string urlString = url.toString();
}
第二种方法,我们可以通过创建一个新的Java对象来构建JObject子类。可以通过调用其他子类的构建器创建新的对象,子类可以使用JNI调用合适的Java构建器。在构建Java对象后,子类就会创建一个新的指向它的全局性引用:
/* 在foo.txt上创建一个新的FileOutputStream。
*/
jace::java::io::FileOutputStream output( "foo.txt" );
无论如何创建JObject子类,子类唯一的操作是对在构建时创建的全局性引用调用DeleteGlobalRef()。
·引用类型的数据可能是空值,通过调用JObject::isNull(),我们可以检测C++代理类是否指向一个为空值的Java对象:
JNIEXPORT void JNICALL Java_foo_Bar_someMethod( JNIEnv *env,jstring
javaString ) {
String str( javaString );
if ( str.isNull() ) {
cout << "Error - The argument, javaString, must not be
null." << endl;
}
}
Throwable和String
Throwable和StringC++代理类都是由JObject派生的(与所有的引用类型的代理类一样),它们(还有其他一些类)是Jace库的核心部份,向用户提供C++和Java之间更紧密的整合能力。
Jace的功能
Jace可以提供许多功能,其中包括线程管理、异常管理、自动类型转换和其他一些功能。下面我们来讨论这些功能:
线程管理
在JNI中存在着一些线程方面的问题:
·JNIEnv指针只能在获得它们的线程上使用。
·大多数的JNI数据类型只能在它们存在的线程上使用。
·在调用JNI函数之前,C++线程必须连接到JVM上
Jace解决了这些问题。第一,Jace库中的每个函数自动地获得一个只能在当前线程上使用的JNIEnv指针。第二,Jace创建必要的JNI类型的全局性引用。例如,JClass创建其jclass成员的全局性引用,JObject创建其jobject成员的全局性引用。与只能供当前线程使用的局部性引用不同的是,全局性变量能够供所有线程使用。
最后,Jace中的每个函数能够确保在调用JNI函数之前,当前的线程能够连接到JVM上。
异常管理
异常处理是JNI编程的一个短肋。在异常处理方面,Jace有二条方针:
1)Jace检查它执行的每个JNI函数的返回码。如果有错误发生,Jace清除JNI异常,然后发出Jace的JNIException消息。
2)如果由于方法发出异常消息,Jace发现Java方法的调用失败,Jace则检查并清除JNI异常,然后创建该异常的一个C++代理实例,并发出C++代理。
using namespace jace::java::net;
void readGoogle() {
try {
/* 当Jace在内部执行NewObject时,它会检查是否有异常发生,
* 如果JNI函数ExceptionOccurred返回一个异常,则Jace清除
* 该异常,创建一个相应的C++代理,并发出它。
*/
URL url( "http://www.google.com" );
}
/* 在这里,我们可以获得Jace发出的C++代理异常
*/
catch ( MalformedURLException& e ) {
cout << e;
}
}
自动类型转换
Jace提供C++和Java简单数据类型之间的自动数据类型转换。我们可以在C++代理需要java::lang::String的地方使用C++的std::string或char*,我们也可以在C++代理方法需要JBoolean、JInt和JChar简单JNI数据类型的地方使用bool、int和char等C++数据类型:
using jace::javax::swing::JFrame;
JFrame createFrame( const std::string& title, int x, int y ){
/* JFrame的原型是JFrame( java::lang::String str );,
* Jace自动地在std::string和java::lang::String之间进行转换。
*/
JFrame frame( title );
/* setLocation的原型是setLocation( JInt x, JInt y );,
* Jace自动地在int之间JInt进行转换。
*/
frame.setLocation( x , y );
return frame;
}
C++集成
Jace包括C++代理生成工具━━BatchGen,Jace开发人员对Java运行时间库环境(JRE)的rt.jar使用BatchGen生成C++代理类。Jace开发人员已经对这些生成的代理类进行修改,以更好地与C++语言和标准库进行集成。
例如,java.lang.Object有一个附加的操作符<<(ostream& out, Object&object),java.lang.String也有一些包括+()、=()和==()在内的附加方法,可以使它与std::strings和char*s更好地进行集成。
类型安全字段和方法访问
C++的代理生成是Java对象类型安全访问的基础。对于给定的Java类文件,Jace能够生成完全相同的方法和字段的C++代理类,我们可以以与调用Java中类似方法相同的方式调用C++代理方法,字段是通过同名的方法进行访问的:
/* 一个Java类
*/
public class Foo {
public int aField;
public String aMethod( URL aURL );
}
/* 从C++中访问C++代理
*/
Foo foo;
foo.aField() = 14;
String result = foo.aMethod( URL( "http://www.google.com" ) );
Jace提供了二种工具━━ProxyGen和BatchGen,我们可以用这二种工具从Java类文件中生成C++代理类。
类型安全数组
我们可以使用Jace的模板JArray类访问Java的数据类型安全数组。根据数组的数据类型,Jace调用合适的Get<Type>ArrayElement()和Set<Type>ArrayElement()JNI函数:
JArray<JInt> intArray( 10 ); // 导致对NewIntArray的调用
int i = intArray[ 2 ]; // 导致对GetIntArrayElements和应用
JArray<String> stringArray( 5 ); // 导致对NewObjectArray的调用
std::string str = stringArray[ 2 ]; //导致对GetObjectArrayElement的调用
Jace工具
ProxyGen和BatchGen可以用来生成C++代理类。ProxyGen用来处理一个类文件,BatchGen则用来处理一个jar文件中所有的类。
ProxyGen
ProxyGen能够将一个Java类文件的头部文件或源文件转出到标准输出。ProxyGen总是会包含生成的C++代理类中的public方法和字段,根据指定的访问水平,它也会包含protected、package或private方法和字段。
用法:ProxyGenerator <类文件> <头部 | 源文件> [ 选项 ]
选项可能是:
-protected :生成protected字段和成员
-package :生成package字段和成员
-private :生成private字段和成员
BatchGen
在生成C++代理类的头文件和源代码文件方面,BatchGen与ProxyGen非常相似。二者的不同之处是,ProxyGen只处理一个Java类文件,BatchGen则处理由多个Java类文件组成的jar或zip文件。另外,ProxyGen将头文件和源代码文件输出到标准输出,BatchGen则将头文件和源代码文件输出到指定的目录。
用法:BatchGenerate <包含Java类的jar或zip>
<头文件的目标目录>
<源代码文件的目标目录>
[ 选项 ]
选项可能是:
-protected :生成protected的字段和成员
-package :生成package字段和成员
-private :生成private字段和成员
Jace还会有哪些改进
将来,Jace的性能会进一步地提高,对数组提供更好的支持,当然了,在其他一些方面也会有所改进。例如,Jace将把Java的数组作为标准的C++容器,并兼容for_each()等函数。另外,它还会支持数组元素的后台缓冲和预先取等功能。
结论
JIN存在的许多问题都与它和C的绑定有关,通过将JNI与C++绑定,Jace很好地解决了JNI存在的问题,将有助于Java的普及。
==============================================================================
项目网址:http://code.google.com/p/jace/
(###)
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
JNI学习(三)、JNI本地方法访问Java端的属性和方法
JNI中C调用Java方法
JNI全攻略之六――操作Java对象的属性
Android JNI开发高级篇
Android JNI开发摘录(六)之JNI线程、NIO、反射处理
Android系统移植与平台开发- JNI介绍
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服