- 浏览: 83124 次
- 性别:
- 来自: 成都
最新评论
-
荆人七十:
谢谢分享~
在eclipse上面搭建Clojure开发运行环境 -
lanily:
楼主,跟你学clojure了,我想请教一个问题:本人事先是用j ...
Clojure细节之form和special form的区别 -
Dead_knight:
http://code.google.com/p/clojur ...
practical_clojure chapter3 控制程序流(未完) -
石建武:
谢谢。提供的离线包。但如果通过‘install new’方式, ...
在eclipse上面搭建Clojure开发运行环境 -
songry:
linkerlin 写道标题里面的 未完 ,啥时候能去掉?可以 ...
practical_clojure chapter3 控制程序流(未完)
编译环境:
fedora16
gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)
准备工作:
首先需要安装jdk和gcc(或者其他c编译器也可以)并配置相应的环境变量(可自行在网上搜索,这个资料很多的),配好之后在命令行运行gcc --version和java -version出现版本号就代表安装配置成功了:
[roysong@roysong c]$ gcc --version gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2) Copyright © 2011 Free Software Foundation, Inc. 本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保; 包括没有适销性和某一专用目的下的适用性担保。 [roysong@roysong c]$ java -version java version "1.6.0_31" Java(TM) SE Runtime Environment (build 1.6.0_31-b04) Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)
然后需要将jdk中的虚拟机库配置到环境路径中,我是在/etc/ld.so.conf.d/下面新建了一个jni-i386.conf文件,内容如下:
/usr/java/jdk1.6.0_31/jre/lib/i386/client
然后运行一下ldconfig –v,让配置文件起作用,也可以直接编辑/etc/ld.so.conf文件,将这个路径加入进去.这个
路径的含义就是$JAVA_HOME/jre/lib/i386/client,其中$JAVA_HOME换成你自己的jdk安装路径就可以了.这是
为了找到libjvm.so.因为我是32位的机器,所以路径中是i386,我估计64位的机器这个目录是不一样的,根据
libjvm.so文件的路径自行替换即可.
程序编写和编译:
程序就分成了c的部分和java的部分,首先我们先看看java的部分,我是把在同一目录下新建了两个文件夹分别叫
c和java,作为c程序的路径和java的classpath,然后在java文件夹下面新建包com.test.base64,java程序的目录
结构即:java/com/test/base64.接着,我们在base64目录下面新建一个Hello.java:
package com.test.base64; public class Hello{ public String hello (String name){ return "hello,"+name; } }
结构非常简单,不过有参数有返回值就可以代表大部分情况了.然后我们使用javac对这个文件进行编译以产生
一个class文件:
[roysong@roysong java]$ cd com/test/base64/ [roysong@roysong base64]$ javac Hello.java [roysong@roysong base64]$ ls Hello.class Hello.java
编译成功后,在base64目录下会出现Hello.class这个文件.
然后我们回到c文件夹,新建一个c源文件,我的是cfjIns.c,开始编写c的程序:
#include <stdio.h> #include <jni.h> /** *初始化jvm */ JNIEnv* create_vm() { JavaVM* jvm; JNIEnv* env; JavaVMInitArgs args; JavaVMOption options[1]; args.version = JNI_VERSION_1_6; args.nOptions = 1; options[0].optionString = "-Djava.class.path=../java"; args.options = options; args.ignoreUnrecognized = JNI_FALSE; JNI_CreateJavaVM(&jvm, (void **)&env, &args); return env; }
这是cfjIns.c的第一部分,头文件声明
#include <jni.h>
非常重要,这是引用的$JAVA_HOME/include/jni.h,一会儿我们在编译时会加上这个路径.
其次就是
args.version = JNI_VERSION_1_6;
这是jni的版本信息,需要跟你自己的jdk中jni版本对应,jdk1.6和jdk1.7的jni版本都是上面这个.
options[0].optionString = "-Djava.class.path=../java";
这个参数是指明你自己java程序的类路径(classpath),因为我的c文件夹和java文件夹在同一目录下,所以我用了
一个相对路径来指明classpath.
cfjIns.c中获取类定义的函数:
/** * 根据全限定类名来获取类的定义 */ jclass create_class(JNIEnv* env,char *className){ jclass cls = (*env)->FindClass(env,className); if(cls == 0){ printf("class-[%s] find error\n",className); return; } return cls; }
这个函数有两个参数,第一个就是我们上面通过create_vm函数创建的jvm环境,第二个是全限定的类名字符串,
比如:"com/test/base64/Hello",返回值即对应的类.
cfjIns.c中获取类实例的函数:
/** *通过无参构造函数来获取对应类的实例 */ jobject getInstance(JNIEnv* env, jclass obj_class) { jmethodID construction_id = (*env)->GetMethodID(env,obj_class, "<init>", "()V"); jobject obj = (*env)->NewObject(env,obj_class, construction_id); if(obj == 0){ printf("java class instance failed\n"); return; } return obj; }
这个函数的第一个参数是jvm环境,第二个是通过上面的create_class函数获取的类定义.
cfjIns.c中获取方法定义的函数:
/** * 根据类\方法名\返回值\参数获取类中对应的方法定义 */ jmethodID get_method(JNIEnv* env,jclass cls,char *methodName,char *key){ jmethodID mid = (*env)->GetMethodID(env,cls,methodName,key); if(mid == 0){ printf("method-%s is not found\n",methodName); return; } return mid; }
这儿我们需要注意的是最后一个参数字符串key,这个是方法签名,用于指明方法的参数和返回值类型,格式是
"(参数类型声明)返回值类型声明".类型声明的值参照下面的对应表:
Java类型 | 对应的签名 |
boolean | Z |
byte | B |
char | C |
shrot | S |
int | I |
long | L |
float | F |
double | D |
void | V |
Object | L用/分割包的完整类名; Ljava/lang/String; |
Array | [签名 [I [Ljava/lang/String; |
举个例子,如果方法的参数是int,返回值是void,那么方法的签名就是"(I)V";如果方法的参数是String,返回值
也是String,则方法的签名就是"(Ljava/lang/String;)Ljava/lang/String;",注意,如果是引用类型,类名后面必须
带有一个分号.
我们也可以通过javap来查看类中方法参数和返回值的签名:
[roysong@roysong c]$ cd ../java/com/test/base64/ [roysong@roysong base64]$ javap -s -private Hello Compiled from "Hello.java" public class com.test.base64.Hello extends java.lang.Object{ public com.test.base64.Hello(); Signature: ()V //构造函数的签名 public java.lang.String hello(java.lang.String); Signature: (Ljava/lang/String;)Ljava/lang/String; //hello方法的签名 }
然后是一个将c语言中的字符串转换为java中String的工具函数:
/** * 转换c中的字符串为java.lang.String,这个方法是从网上找到的,感谢原作者天末凉风 */ jstring stoJstring(JNIEnv* env, const char* pat) { jclass strClass = (*env)->FindClass(env,"Ljava/lang/String;"); jmethodID ctorID = (*env)->GetMethodID(env,strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = (*env)->NewByteArray(env,strlen(pat)); (*env)->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = (*env)->NewStringUTF(env,"utf-8"); return (jstring)(*env)->NewObject(env,strClass, ctorID, bytes, encoding); }
有了上面的例子,这个函数就很好理解了,首先获取到java.lang.String的类定义,然后获取构造函数,然后将
c中的字符串转化为字节流并设置编码格式,最后产生一个新的java.lang.String对象并返回.
这下我们就准备齐全了,开始编写调用函数:
void invoke(){ JNIEnv* env = create_vm(); //初始化java虚拟机 jclass cls = create_class(env,"com/test/base64/Hello");//根据类名找到对应的类 jobject obj = getInstance(env,cls);//然后根据类获取对应的实例 jmethodID hello = get_method(env,cls,"hello","(Ljava/lang/String;)Ljava/lang/String;");//根据类\方法名和签名获取到对应的方法 jstring name_str = (*env)->CallObjectMethod(env,obj,hello,stoJstring(env,"a"));//传入参数调用方法 const char* pname = (*env)->GetStringUTFChars(env,name_str, NULL);//将返回的java字符串转换为c字符串 printf("the result is:%s\n",pname);//打印出调用的结果 }
调用函数编写完成后,用一个main函数来运行一下查看结果:
int main(int argc, char **argv) { invoke(); }
至此,c的源代码cfjIns.c就编写完成了,我们开始编译cfjIns.c:
[roysong@roysong c]$ gcc -o cfj cfj.c -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -L$JAVA_HOME/jre/lib/i386/client -ljvm [roysong@roysong c]$ ls cfjIns cfjIns.c
编译命令中我们使用-I选项加入了两个头文件路径,$JAVA_HOME/include这个是为了引用到jni.h,而
$JAVA_HOME/include/linux这个为了引用到jni_md.h,因为jni.h中有对jni_md.h的引用.我的操作系统是fedora,所以
jni_md.h在$JAVA_HOME/include的linux文件夹下面,其他操作系统可能路径不同,找到jni_md.h文件的路径进行
替换即可.使用-L选项是为了引用到java虚拟机的库文件libjvm.so,-l选项(注意,这是小写的L,而不是大写的i)是声明
具体引用的库jvm.如果libjvm.so文件的路径与我的不同,找到libjvm.so文件的路径进行替换即可.
编译成功后,目录下面会出现一个可运行的cfj文件,如果一切顺利,我们运行它就可以得到预期的结果:
[roysong@roysong c]$ ./cfjIns hello,a
显示正常,调用完成
发表评论
-
Clojure入门-前言
2012-04-10 09:04 0前言 0.1 关于Clojure 在正式接触Clo ... -
clojure API学习(4) 程序流控制
2011-12-20 16:38 0本文基于jdk1.6和clojure1.2 if ... -
clojure API学习(3) 字符和字符串操作
2011-12-16 00:08 4602注:本文基于jdk1.6,clojur ... -
clojure的冒泡排序实现
2011-12-08 17:11 2150冒泡排序的基本概念是:依次比较相邻的两个数,将小数放在 ... -
Clojure-JVM上的函数式编程语言(17)数据库 作者: R. Mark Volkmann
2011-11-26 09:20 1797原帖地址:http://java.ociweb.com/mar ... -
clojure API学习(2) 比较操作
2011-11-26 08:59 2630注:本文基于jdk1.6,clojur ... -
clojure API学习(1) 数学运算
2011-11-24 22:32 2998附注:本文基于jdk1.6,clo ... -
Clojure-JVM上的函数式编程语言(16)Web应用 作者: R. Mark Volkmann
2011-11-19 11:37 1959原帖地址:http://java.ociweb.com/mar ... -
Clojure-JVM上的函数式编程语言(15) 桌面应用 作者: R. Mark Volkmann
2011-11-19 11:04 1512原帖地址:http://java.ociweb.com/mar ... -
Clojure-JVM上的函数式编程语言(14) 编辑器和开发环境 作者: R. Mark Volkmann
2011-11-19 10:31 1674原帖地址:http://java.ociweb.com/mar ... -
Clojure-JVM上的函数式编程语言(13)自动化测试 作者: R. Mark Volkmann
2011-11-17 21:25 1384原帖地址:http://java.ociweb.com/ma ... -
如何将Clojure作为java工程中的脚本
2011-11-17 09:57 0注:本文基于jdk1.6和Clojure1.2 -
Clojure与java设计模式(3) 策略模式
2011-11-16 15:45 1815注:本文基于jdk1.6和Clojure1.2 策略模 ... -
在eclipse上面搭建Clojure开发运行环境
2011-11-15 13:30 12460首先,需要下 ... -
Clojure和JAVA设计模式(2) 工厂模式之工厂方法
2011-11-15 15:05 1248注:本文基于jdk1.6和Clojure1.2 工厂方 ... -
Clojure和JAVA设计模式(1) 工厂模式之简单工厂
2011-11-12 15:24 2174序 在java中,设计模式是多态和封装的重要表 ...
相关推荐
这是一个简单的JNI开发中C层调用Java层函数的事例工程,对应的博客地址是:http://blog.csdn.net/hty1053240123/article/details/52126386
android JNI C 调用Java
c++通过jni调用java类. jdk1.6下通过测试
JNI中C代码调用java的小示例。android studio2.3.3开发
次资源完美的实现linux环境下C语言调用java类里面的函数,包括jdk的下载安装配置,java类的编写编译,C语言调用java类的demo代码实现,代码的编译及测试。
完整的实现java跨平台调用C程序源码,包含JAVA源码和C源码以及编译后的demo dll。将dll放到jdk bin目录下,java 项目可以直接运行。若要修改dll可以,修改C源码后重新编译生成dll。该demo处理了多线程调用c,全局...
利用JNI技术实现Java中调用C++编写的函数库示例程序源码,并附上参考JNI文档。...详情见本人博客:Java学习之通过JNI调用C/C++编写的dll链接库(图文教程)(http://write.blog.csdn.net/postlist)
最近项目中需要使用JNI,所以研究了一下,其中遇到过不少问题,总结一下,让遇到同样问题的人...在C/C++中调用Java的方法一般分为五个步骤:初始化虚拟机、获取类、获取类的方法、创建类对象、调用方法和退出虚拟机。
Java通过JNI调用C语言函数库的方法
NDK的配置过程在这里就不讲了,这个demo主要实现的是通过jni技术,我们让java调用c代码。
JNI 调用 c pp互相调用实例, java JNI cpp互相调用实例
JNI的示例代码, C的代码如何调用Java的对象的方法。编译环境:cygwin, makefile. 纯C
假如有一个现有的 .dll/.so 文件,假如使用 JNI 技术调用,我们首先需要另外使用 C 语言写一个 .dll/.so 共享库,使用 SUN 规定的数据结构替换 C 语言的数据结构,调用已有的 ? dll/so 中公布的函数。 然后再在 Java...
Java通过JNI调用C语言的方法[参考].pdf
Java jni调用c实例
Java通过JNI调用C函数Demo 需要的人下载看看
通过jni完成java调用c/c++,包含c/c++生成Dll动态库
android jni使用java、c c++相互调用
用c语言写了一个清屏的函数,然后生成动态链接库,再通过jni在java里调用清屏函数。请注意代码中的函数JNIEXPORT void JNICALL Java_Test_cls(JNIEnv *env, jobject obj),函数名字的Test是指使用该函数的Java类的...
在C语言中调用java语言的方法!对初学者很不错的实例!Android jni开发之C语言调用java语言