编译环境:

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

#include

/**

*初始化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

非常重要,这是引用的$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, "", "()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, "", "([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

显示正常,调用完成

c 通过jni调用java_使用c通过jni调用java相关推荐

  1. python中如何调用java_在Python程序中调用Java代码的实现

    有头发且有趣的码农万里挑一~ 14 有料叔 | 一位有故事的程序猿 前 言 开发Python程序,需求中需要用到Java码,Python作为"胶水"语言能够很好的实现这种需求.具体 ...

  2. cocos lua调用java_【Tech-Lua】Cocos-2dx-Lua调用java的小白教程(三)

    上周五下班前,打包成功了.我很高兴,周六去踢场足球,周日去现场看了最后一分钟掉球的恒大,度过了一个愉快的周末.然后,噩梦的周一开始了. 我再次打包,打算打包就安装,但结果是失败的.为何?我周五明明成功 ...

  3. rust 调用 java_自从尝了 Rust,Java 突然不香了

    Rust 是软件行业中相对而言比较新的一门编程语言,如果从语法上来比较,该语言与 C++ 其实非常类似,但从另一方面而言,Rust 能更高效地提供许多功能来保证性能和安全.而且,Rust 还能在无需使 ...

  4. android jni 调用java_Android 基于NDK的JNI开发 C调用java和java调用C

    首先,这里所要讲的主要是C调用java的,因为这里网上也没有一个详细的资料,其次,我开发android应用没多久,其中java错误敬请谅解! 关于配置NDK环境问题应该不用再赘述了,这个网上有很多,只 ...

  5. UNITY调用安桌方法出现 JNI: Init'd AndroidJavaClass with null ptr!

    UNITY调用安桌方法出现 JNI: Init'd AndroidJavaClass with null ptr! 原因是····· 得运行在一个真正的Android设备上! 得运行在一个真正的And ...

  6. android sudio jni 调用so_UE4:UPL 与 JNI 调用的最佳实践

    本篇文章搬运自我自己的博客,原文链接: https://imzlp.me/posts/27289/ 作者: 查利鹏 在使用UE4开发Android时,有时需要获取平台相关的信息.或者执行平台相关的操作 ...

  7. android sudio jni 调用so_Android NDK-深入理解JNI

    Java调用C/C++在Java语言里面本来就有的,并非Android独有的,即JNI.JNI就是Java调用C++的规范. JNI 概述 JNI,全称为Java Native Interface,即 ...

  8. nashorn js 调用 java_从nashorn(JDK 8 JavaScript引擎)调用char []输入参数调用Java函数?...

    我想从Oracle的nashorn JavaScript引擎中调用一个带有 char[] 输入参数的Java函数(非数组参数类型的函数对我来说没问题) . 如果我用JavaScript字符串文字调用J ...

  9. kotlin 调用java_从Kotlin调用Java代码

    Kotlin代码与Java代码完全兼容. Java代码在Kotlin代码中轻松调用,Kotlin代码也可以通过Java代码的正常方式调用. 从Kotlin调用Java代码 从Kotlin文件调用Jav ...

最新文章

  1. 网络共享服务(一)之FTP
  2. 十、图像参数集Picture Paramater Set(PPS)解析
  3. unity游戏框架学习-框架结构
  4. Struts2 源码分析——拦截器的机制
  5. solidworks小金球_如何在没有电缆的情况下传送第77届年度金球奖
  6. python实验二报告_20172304 2019-2020-2 《Python程序设计》实验二报告
  7. 电脑运行java游戏,电脑运行软件卡顿?这几招游戏或是办公,让你速度飞起!...
  8. asp.net Page.Controls对象(找到所有服务器控件)
  9. python网课答案查询_中国大学慕课Python编程基础期末考试查题公众号答案
  10. HTML readonly
  11. 惠普继续大裁员:未来3年计划裁撤7000-9000个岗位
  12. matlab练习程序(图像序列合成视频)
  13. spring in action 4 第6章 视图分发
  14. java移库数据同步,洗车管理系统会员管理+门店店务同步管理
  15. C语言程序设计--新生入学登记系统
  16. android qq毛玻璃,如何快速做出毛玻璃背景?有了这个网格渐变神器,1分钟搞定...
  17. ffmpeg之 一张/多张图片合成视频
  18. 个人网站建设教程|本地网站环境搭建|网站制作教程
  19. Win11磁盘被写保护怎么解除?
  20. STM32F103_study64_The punctual atoms(Simulator and downloader)

热门文章

  1. 系统美化 XP主题及其他
  2. ASP.NET MVC从数据库读取、存入图片
  3. IDEA查看源码时总是出现.class而不是.java源码(解决办法)
  4. 为什么MySQL索引更适合B+树而不是二叉树、B树
  5. wordpress html页面缓存 cdn,WordPress 下老旧又高效的本地缓存插件 cos-html-cache
  6. sql管理:索引超出范围必须为非负值并小于集合大小_java面试基础知识-数据库基础知识(数据库索引部分)...
  7. anaconda中安装xgboost_Anaconda是什么?Anconda下载安装教程 - python基础入门(16)
  8. Win7电脑创建本地连接网络的操作方法
  9. 华硕台式机重装系统教程方法
  10. JavaScript 的 Date 方法的使用