clq
浏览(1) +
2007-09-15 23:01:37 发表
编辑
关键字:
java 与 C 的相互调用原来如此简单! 今天无意在书城看到了一本 sun 认证的高级书说是什么第7版本(7还是5?)其中调用C那一章节比我以前知道的可简单得多了,而且还有个C调用java函数的例子. 难道是因为它用的 j2se 1.5 的原因? 它也有个 loadlibrary 函数调用 C 的dll.
clq
Java基础知识——JNI入门介绍(上) zt 学习了一下JNI,发表文章的时候不知道该选什么好了,不知道JNI应该属于那个范畴^_^。 1.简介 JNI是Java Native Interface的缩写,它的设计目的是: The standard Java class library may not support the platform-dependent features needed by your application. You may already have a library or application written in another programming language and you wish to make it accessible to Java applications You may want to implement a small portion of time-critical code in a lower-level programming language, such as assembly, and then have your Java application call these functions 2.JNI的书写步骤 编写带有native声明的方法的java类 使用javac命令编译所编写的java类 使用javah ?jni java类名生成扩展名为h的头文件 使用C/C++实现本地方法 将C/C++编写的文件生成动态连接库 ok 1) 编写java程序: 这里以HelloWorld为例。 代码1: class HelloWorld { public native void displayHelloWorld(); static { System.loadLibrary("hello"); } public static void main(String[] args) { new HelloWorld().displayHelloWorld(); } } 声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。 Load动态库:System.loadLibrary("hello");加载动态库(我们可以这样理解:我们的方法displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“hello”是动态库的名字。 main()方法 2) 编译没有什么好说的了 javac HelloWorld.java 3) 生成扩展名为h的头文件 javah ?jni HelloWorld 头文件的内容: /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class HelloWorld */ #ifndef _Included_HelloWorld #define _Included_HelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: HelloWorld * Method: displayHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif (这里我们可以这样理解:这个h文件相当于我们在java里面的接口,这里声明了一个Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致)。 4) 编写本地方法 实现和由javah命令生成的头文件里面声明的方法名相同的方法。 代码2: 1 #include 2 #include "HelloWorld.h" 3 #include 4 JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj) { printf("Hello world!\n"); return; } 注意代码2中的第1行,需要将jni.h(该文件可以在%JAVA_HOME%/include文件夹下面找到)文件引入,因为在程序中的JNIEnv、jobject等类型都是在该头文件中定义的;另外在第2行需要将HelloWorld.h头文件引入(我是这么理解的:相当于我们在编写java程序的时候,实现一个接口的话需要声明才可以,这里就是将HelloWorld.h头文件里面声明的方法加以实现。当然不一定是这样)。然后保存为HelloWorldImpl.c就ok了。 5) 生成动态库 这里以在Windows中为例,需要生成dll文件。在保存HelloWorldImpl.c文件夹下面,使用VC的编译器cl成。 cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll 注意:生成的dll文件名在选项-Fe后面配置,这里是hello,因为在HelloWorld.java文件中我们loadLibary的时候使用的名字是hello。当然这里修改之后那里也需要修改。另外需要将-I%java_home%\include -I%java_home%\include\win32参数加上,因为在第四步里面编写本地方法的时候引入了jni.h文件。 6) 运行程序 java HelloWorld就ok。^_^
clq
JNI之C++调用Java类 ——java.lang.String [zt] JNI之C++调用Java类 ——java.lang.String 为什么要用C++调用Java类?很难回答,写着文章只是觉得JNI很有意思。于是开始编写一段使用VC++在Windows系统里调用java的String类,在C++里调用String类内的一些方法。 JNI已经被开发了很多年,而在我2年多的Java编程时间里从来没有接触过。直到最近研究JVM实现原理才注意到JNI。 JNI既Java Native Interface,Native这个词我见过我认为最恰当的翻译就是原生。原生的意思就是来自系统自己的,原汁原味的东西,例如Win32 API。Java类需要在虚拟机上运行,也就不是原生的,同样.NET Framework也不是原生的。JNI也就是Java原生接口。关于JNI的规范,以及为什么要使用它,它能做些什么,都在http://java.sun.com/j2se/1.4.2/docs/guide/jni/spec/jniTOC.html里记述着。 JNI是规范,它规定了虚拟机的接口,而把具体的实现留给开发者。 JVM的实现不是唯一的,目前存在很多种Java虚拟机,Sun Hotspot,IBM JDK,还有HP的,Kaffe等等。最流行的就是Sun的Hotspot,最复杂的就是IBM JDK,这是IBM的一贯作风。本文不讨论JVM的实现,只关注JNI。如果您安装了Sun的JDK,您就能在[JAVA_HOME]\include目录下找到jni.h。这个头文件就是虚拟机的唯一接口,你可以调用它声明的函数创建一个JVM。 在说明C++调用Java类之前,我想先演示一下如果编写Java Native Method。 1.编写带有Native方法的Java类 package org.colimas.jni.test; public class JniTest { static { System.loadLibrary("JniTestImpl"); } //JVM调用JniTestImpl.dll public JniTest(){ } //原生方法 public native void print(String str); /** * @param args */ public static void main(String[] args) { JniTest test=new JniTest(); test.print("hello JVM"); //调用原生方法 } } 2.使用javah生成c语言头文件。 javah -jni org.colimas.jni.test.JniTest 目录里多了一个org_colimas_jni_test_JniTest.h文件,打开文件,内容如下: /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_colimas_jni_test_JniTest */ #ifndef _Included_org_colimas_jni_test_JniTest #define _Included_org_colimas_jni_test_JniTest #ifdef __cplusplus extern "C" { #endif /* * Class: org_colimas_jni_test_JniTest * Method: print * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_org_colimas_jni_test_JniTest_print (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif 其中的Java_org_colimas_jni_test_JniTest_print就是JniTest类里面的print原生方法的C语言声明。 3.编写C代码实现原生方法print #include #include "org_colimas_jni_test_JniTest.h" //javah生成的头文件 #include JNIEXPORT void JNICALL Java_org_colimas_jni_test_JniTest_print (JNIEnv *env, jobject object,jstring str) { //获得字符串 const char * txt=(*env)->GetStringUTFChars(env,str,0); printf("%s\n",txt); //打印到控制台 return; } 参数JNIEnv *env,是JNI里最重要的变量。Java.exe创建JVM,之后JVM生成一个env,该env相当于JVM内的Session,可以完成创建Java对象,调用类方法,获得类的属性等等。 在这里env将方法的参数Str从JNI的jstring类型转换为常数char数组。 4.编译 cl /Ic:\j2sdk1.4.2_10\include /Ic:\j2sdk1.4.2_10\include\win32 /c JniTestImpl.c 5.连接为DLL link /dll JniTestImpl.obj 6.设置PATH set PATH=C:\MyProject\Colimas\CD\JNI\MyJNI;%PATH% 7.运行 java org.colimas.jni.test.JniTest 返回结果 hello JVM 结束 以上是实现Java原生方法的开发过程,下面进入正题,使用C++调用Java的java.lang.String类。 1. Object类出创建JVM。 使用Java类之前必须要创建JVM环境。JDK由java.exe来完成。本文有Object类的静态方法BeginJVM来创建,用EndJVM来关闭。 创建JVM之后会在创建2个变量,分别是JNIEnv* env和JavaVM* jvm,JNIEnv上文已经说明,JavaVM,顾名思义,代表Java虚拟机,用它来关闭JVM。 Object类的头文件 #include "jni.h" class Object { public: static bool BeginJVM(); static bool EndJVM(); Object(); virtual ~Object(); protected: static JNIEnv* env; static JavaVM* jvm; }; object.cpp代码 #include "stdafx.h" #include "JavaClasses.h" #include "Object.h" Object::Object() {} Object::~Object() {} JNIEnv* Object::env=NULL; JavaVM* Object::jvm=NULL; //创建JVM bool Object::BeginJVM() { JavaVMOption options[3]; JavaVMInitArgs vm_args; //各种参数 options[0].optionString="-Xmx128m"; options[1].optionString="-Verbose:gc"; options[2].optionString="-Djava.class.path=."; vm_args.version=JNI_VERSION_1_2; vm_args.options=options; vm_args.nOptions=3; //创建JVM,获得jvm和env int res = JNI_CreateJavaVM(&jvm,(void **)&env, &vm_args); return true; } bool Object::EndJVM() { //关闭JVM jvm->DestroyJavaVM(); return true; } 2. C++的String类调用java.lang.String类方法 编写C++版的String类,调用java String类方法。调用的方法如下: String replaceAll(String regex, String replacement); boolean endsWith(String str); int indexOf(String str); int compareTo(String anotherString); char charAt(int i); String的头文件: class String :public Object { public: //与要调用的Java方法名一致。 const char * replaceAll(char *regex,char *replacement); bool endsWith(char * str); int indexOf(char * str); int compareTo(char *anotherString); char charAt(int i); String(char *str); virtual ~String(); }; 实现: #include "stdafx.h" #include "String.h" #include "jni.h" using namespace std; jclass clazz; //全局变量,用来传递class jobject object; //全局变量,用来传递object String::String(char *str) { jstring jstr; if (Object::env ==NULL) { cout << "JVM is not created" << endl; exit(-1); } //获得java.lang.String类 clazz=Object::env->FindClass("java/lang/String"); if (clazz ==0 ){ cout << "Class is not found" << endl; exit(-1); } //获得String(String str)构造体 jmethodID mid= Object::env->GetMethodID(clazz,"", "(Ljava/lang/String;)V"); if (mid==0){ cerr<< "GetMethodID Error for class" << endl; exit(-1); } //江字符串封装为jstring。 jstr = Object::env->NewStringUTF(str); if (jstr == 0) { cerr << "Out of memory" < exit(-1); } cout << "invoking method" << endl; //创建一个java.lang.String对象。 object=Object::env->NewObject(clazz,mid,jstr); } String::~String() {} char String::charAt(int i) { if (Object::env ==NULL) { cout << "JVM is not created" << endl; exit(-1); } if (clazz ==0 ){ cout << "Class is not found" << endl; exit(-1); } if (object ==0 ){ cout << "String object is not created" << endl; exit(-1); } jmethodID mid; //获得charAt方法,(I)C表示 参数为int型,返回char型。详细参见JNI规范 mid= Object::env->GetMethodID(clazz,"charAt", "(I)C"); if (mid==0){ cerr<< "GetMethodID Error for class" << endl; exit(-1); } jint ji=i; cout << "invoking method" << endl; //调用charAt jchar z=Object::env->CallCharMethod(object,mid,i); //返回结果。 return z; } int String::compareTo(char *anotherString) { if (Object::env ==NULL) { cout << "JVM is not created" << endl; exit(-1); } if (clazz ==0 ){ cout << "Class is not found" << endl; exit(-1); } if (object ==0 ){ cout << "String object is not created" << endl; exit(-1); } jmethodID mid; //(Ljava/lang/String;)I表示参数为java.lang.String,返回int mid= Object::env->GetMethodID(clazz,"compareTo", "(Ljava/lang/String;)I"); if (mid==0){ cerr<< "GetMethodID Error for class" << endl; exit(-1); } jstring jstr = Object::env->NewStringUTF(anotherString); cout << "invoking method" << endl; //调用方法 jint z=Object::env->CallIntMethod(object,mid,jstr); //返回结果 return z; } int String::indexOf(char *str) { if (Object::env ==NULL) { cout << "JVM is not created" << endl; exit(-1); } if (clazz ==0 ){ cout << "Class is not found" << endl; exit(-1); } if (object ==0 ){ cout << "String object is not created" << endl; exit(-1); } jmethodID mid; mid= Object::env->GetMethodID(clazz,"indexOf", "(Ljava/lang/String;)I"); if (mid==0){ cerr<< "GetMethodID Error for class" << endl; exit(-1); } jstring jstr = Object::env->NewStringUTF(str); cout << "invoking method" << endl; jint z=Object::env->CallIntMethod(object,mid,jstr); return z; } bool String::endsWith(char *str) { if (Object::env ==NULL) { cout << "JVM is not created" << endl; exit(-1); } if (clazz ==0 ){ cout << "Class is not found" << endl; exit(-1); } if (object ==0 ){ cout << "String object is not created" << endl; exit(-1); } jmethodID mid; mid= Object::env->GetMethodID(clazz,"endsWith", "(Ljava/lang/String;)Z"); if (mid==0){ cerr<< "GetMethodID Error for class" << endl; exit(-1); } jstring jstr = Object::env->NewStringUTF(str); cout << "invoking method" << endl; bool z=Object::env->CallBooleanMethod(object,mid,jstr); return z; } const char * String::replaceAll(char *regex, char *replacement) { if (Object::env ==NULL) { cout << "JVM is not created" << endl; exit(-1); } if (clazz ==0 ){ cout << "Class is not found" << endl; exit(-1); } if (object ==0 ){ cout << "String object is not created" << endl; exit(-1); } jmethodID mid; mid= Object::env->GetMethodID(clazz,"replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); if (mid==0){ cerr<< "GetMethodID Error for class" << endl; exit(-1); } jvalue array[2]; jstring jreg = Object::env->NewStringUTF(regex); jstring jstr = Object::env->NewStringUTF(replacement); array[0].l=jreg; array[1].l=jstr; cout << "invoking method" << endl; //传入参数,调用replaceAll方法 jobject z=Object::env->CallObjectMethodA(object,mid,array); const char *result=Object::env->GetStringUTFChars((jstring)z, 0); return (const char *)result; } 3.测试 编写测试代码 using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { //创建JVM Object::BeginJVM(); String test("hello"); //调用replaceAll const char *result=test.replaceAll("l","z"); //返回结果 cout<< result < //关闭JVM Object::EndJVM(); } return nRetCode; } 4.运行 编译需要 jni.h和jvm.lib文件。 jni.h在[JAVA_HOME]\include jvm.lib在[JAVA_HOME]\lib 运行需要jvm.dll jvm.dll在[JAVA_HOME]\ jre\bin\client 运行结果如下: invoking method invoking method hezzo Press any key to continue 尽管本文的代码很有意思,但我还没有想到有什么价值,以及应用到实际项目中的理由。
clq
来自 http://blog.csdn.net/doomhuntercj/archive/2007/04/20/1572544.aspx linux下 使用JNI 来以C++调用JAVA的类!(一) 决定使用JNI,实际是为了能够将通讯与调用后台的lucene索引,因此老大决定要采用这种方式来实现index的多机分布式的索引服务。接到任务,使用C++来调用Lucene的java查询的封装类。 用了java,c++各一段时间,却从未接触过JNI. 开始从网上收集该方面的资料,从头开始没有指导的时候特别郁闷,网上找到的大多数资料是讲述如何用java来调用C++的,而且多试windows下以dll方式为java提供动态库的,JAVA是宿主,而我要的是C++为宿主,linux环境下的。长期是各种环境设置,头文件查找,lib库的指定等等东西常常搞得人焦头烂额!一天时间总算摸清了怎么将虚拟机调起来(狂汗!!!),又一天时间总算摸清了如何进行调用java中的类和函数。基本可以开始JNI的程序编写里程了:)现记录自己的心得如下: 首先,来了解JNI是干什么的。Java Native Interface(JNI)是Java语言的本地编程接口,是J2SDK的一部分。在java程序中,我们可以通过JNI实现一些用java语言不便实现的功能。通常有以下几种情况我们需要使用JNI来实现。 你希望使用一些已经有的类库或者应用程序,而他们并非用java语言编写的 程序的某些部分对速度要求比较苛刻,你选择用汇编或者c语言来实现并在java语言中调用他们。 另外,也可是使用很多本地的程序来使用JAVA的类。 也就是说JNI就是跟大家进行交互的接口,其已经作为标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。 不管怎么说,JNI是规范,它规定了虚拟机的接口,而把具体的实现留给开发者。 我的环境,也就是本文的条件是:LINUX RH EL4,装了JDK1.5.0的版本。 要使用JVM来调用java的类别,首先得让java虚拟机启动起来。如何启动?C++如何来调用? 首先,得找到JNI的头文件,一般为装java的目录,如(/usr/java/jdk1.5.0/include/)里的jni.h,linux下大家要牢记G++编译的时候要加上/usr/java/jdk1.5.0/include/linux和上面两个目录,否则一大堆的undefined …! 下面帖一段我自己环境下的代码: //创建JVM JNIEnv* env=NULL; JavaVM* jvm=NULL; bool Object::BeginJVM() { JavaVMOption options[4]; JavaVMInitArgs vm_args; //各种参数 options[0].optionString="-Xmx512m"; options[1].optionString="-Xms128m"; //大家注意这里为你需要使用的java类class所在的目录 options[2].optionString="-Djava.class.path=.:../lucene-core-2.0.0.jar"; options[3].optionString="-Djava.compiler=NONE"; //该地方我也不太清除,我的JDK版本是1.5.0,但好像没有JNI_VERSION_1_5 vm_args.version=JNI_VERSION_1_4; vm_args.options=options; vm_args.nOptions=4; //创建JVM,获得jvm和env int res = JNI_CreateJavaVM(&jvm,(void **)&env, &vm_args); if(res == JNI_ERR) { printf("Error invoking the JVM"); return false; } return true; } bool Object::EndJVM() { //关闭JVM jvm->DestroyJavaVM(); return true; } 注意编译的时候,要加上-I指定头文件目录,-L指定libjvm.so的所在目录,如我的: g++ -I /usr/java/jdk1.5.0/include/ -I /usr/java/jdk1.5.0/include/linux/ -L /usr/java/jdk1.5.0/jre/lib/i386/server/ -o startJVM startJVM.cpp -ljvm 这样至少可以以C/C++的方式启动了java的JVM,下篇将是实际读取,调用java中的数据和接口。 对了有些可能会碰到的出错问题:第一,libjvm.so :no such file or directory. 第二,就是N多的undefine ....的 总结下来,第一种是因为没有加入-L 指明lib所在路径,并加上-ljvm ;第二种是由于没有加入-I 指明头文件和linux下目录的头文件。 另外,在运行的时候,可能会报错cannot find shared lib:libjvm.so,问题是没有加入run_lib_directory,也就是说要设置LD_LIBRARY_PATH。设置的命令为:export LD_LIBRARY_PATH=/usr/java/jdk1.5.0/jre/lib/i386/serve
clq
JNI:如何在C++中调用Java Method? 楼主menlion(浪·星魂)2002-10-08 09:51:51 在 Java / J2SE / 基础类 提问 Java里的代码如下: public class Mail { ... static public void printInfo(String str) { System.out.println(str); } native static public void invokeJavaMethod(); ... } C++里部分代码如下: BTW:该方法是Java里的一个native方法的实现。 /* * Class: agent_Mail * Method: invokeJavaMethod * Signature: ()V */ JNIEXPORT void JNICALL Java_Mail_invokeJavaMethod (JNIEnv *env, jclass obj) { jmethodID mid = env->GetMethodID(obj, "printInfo", "(Ljava/lang/String;)V"); if(mid == 0) { printf("Can't find method printInfo!\n"); return; } env->ExceptionClear(); //??????? //env->MonitorEnter(); env->CallVoidMethod(obj, mid, env->NewStringUTF("Hello world!")); } 问题1:每次运行程序的时候,C++程序都打印"Can't find method printInfo!",为什么?怎么解决? 问题2:env->MonitorEnter(),这个方法是干什么用的?完整的用法该怎么写? 问题点数:100、回复次数:8 Top 1 楼xiaosongyu(松) 回复于 2002-10-08 10:25:28 得分 0 static public void printInfo(String str) 你的java方法是static的,应该使用GetStaticMethodID来获取方法的ID Top 2 楼menlion(浪·星魂) 回复于 2002-10-08 10:48:01 得分 0 还是不行,报java.lang.IncompatibleClassChangeError错误。:( Top 3 楼menlion(浪·星魂) 回复于 2002-10-08 11:23:10 得分 0 这一次是这一行报错: env->CallVoidMethod(obj, mid, env->NewStringUTF("Hello world!")); 这一行有什么错误吗? Top 4 楼xiaosongyu(松) 回复于 2002-10-08 11:29:21 得分 0 把你调用 native static public void invokeJavaMethod(); 这个函数的代码贴出来看看,OK? Top 5 楼xiaosongyu(松) 回复于 2002-10-08 11:34:08 得分 0 呵呵,原来如此 改成CallStaticVoildMethod试试看 Top 6 楼menlion(浪·星魂) 回复于 2002-10-08 11:51:28 得分 0 我对我的愚笨实在无以言表,多谢了! 另外,你能谈谈你对 问题2:env->MonitorEnter(),这个方法是干什么用的?完整的用法该怎么写? 的看法吗? 谢谢! Top 7 楼xiaosongyu(松) 回复于 2002-10-08 13:20:18 得分 0 MonitorEnter应该是用于线程同步的,你可以参考JNI的文档,以下是我的理解,仅供参考: 需要同步的代码在执行的时候应该锁定一个对象,在JNI代码中这样使用 jint retcode = env->MonitorEnter(env, monObj); if(retcode==0) { /*需要线程同步的代码在这里 ...... */ env->MonitorExit(env, monObj) } 其中monObj是一个java对象。 关于Monitor的信息,你可以参考java语言规范 http://java.sun.com/docs/books/jls/second_edition/html/memory.doc.html#28270 或许对你有一些帮助,因为我没有用过这个函数,所以还得你自己摸索。 Top 8 楼xiaosongyu(松) 回复于 2002-10-08 13:22:23 得分 100 上面仅仅是我的理解,对错与否没有经过验证,供参考吧
NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.