Jni:架起 Java 和 C/C++的桥梁,Java Native Interface
来源:互联网 发布:从事数据可视化的公司 编辑:程序博客网 时间:2024/06/03 02:09
前言
正如标题所说,Jni(Java Native Interface) 把 Java 和 C/C++之间联系起来了。这样的话,Java可以直接调用C/C++语言编写的代码。Jni的主要问题在于破坏JVM的跨平台特征。Jni的优点在于:C/C++运行快一些;可以利用C/C++的历史遗留代码;有时候只能用C/C++来完成项目需求。
图:Java jni 示意图,引自华清远见的新浪博客。
Talk is cheap, show me the code. 下面的代码是在Linux环境下写的。总的来说,是一个五步曲,按顺序依次分为:Java代码、编译Java、C代码、编译C、运行。在关键部分做了详细的说明。
注:使用Linux操作系统,下面的第一~第五步中的文件和操作都是在同一个目录/home/Jni_example下面,即项目的根目录。
第一步:Java代码
// 文件:/home/Jni_example/Hello.javapublic class Hello{ public native void sayHi(String who, int times); static { System.loadLibrary("HelloC"); } public static void main(String[] args) { Hello hello = new Hello(); System.out.println("Say hello to " + args[0] + " " + args[1] + " times." ); hello.sayHi(args[0], Integer.parseInt(args[1])); }}
Java程序通过如下方法调用C的代码:
public native void sayHi(String who, int times);
Java程序加载C语言编写的动态链接库需要loadLibrary。这个动态库不能随便自己起名字!如果你在Java代码中System.loadLibrary(“HelloC”); 那么C的动态库的名字必须是libHelloC.so,从”HelloC” 到 “libHelloC.so”,一个字母不能多,一个字母不能少。
static{ System.loadLibrary("HelloC");}
其实还可以使用 System.load()方法,请看此博客最底下的讨论部分。
第二步:编译Java
前提是,你要安装好 Java JDK,不然没有 javac 和 javah 这两个编译程序
javac Hello.java // 编译 Java 程序,生成Hello.classjavah -jni Hello // 产生一个 Hello.h 头文件,这个头文件包含了关键的native接口函数的声明。
javah 自动生成的Hello.h头文件如下:
// 文件:/home/Jni_example/Hello.h/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class Hello */#ifndef _Included_Hello#define _Included_Hello#ifdef __cplusplusextern "C" {#endif/* * Class: Hello * Method: sayHi * Signature: (Ljava/lang/String;I)V */JNIEXPORT void JNICALL Java_Hello_sayHi (JNIEnv *, jobject, jstring, jint);#ifdef __cplusplus}#endif#endif
第三步:C代码
C程序 Hello.c,用来实现 Hello.h 里面的函数
// 文件:/home/Jni_example/Hello.c#include <stdio.h>#include "Hello.h"JNIEXPORT void JNICALL Java_Hello_sayHi(JNIEnv * env, jobject obj, jstring who, jint times){ jint i; jboolean iscopy; const char *name; name = (*env)->GetStringUTFChars(env, who, &iscopy); for(i = 0; i < times; i++) { printf("Hello %s\n", name); }}
这里的 jint 和 jboolean 你可能没有见过。他们其实就是在 JAVA_HOME/include/jni.h 和 JAVA_HOME/include/linux/jni_md.h 头文件里面通过typedef定义的数据类型,你可以自己去看一看。
C程序和Java程序之间的参数传递是一个比较麻烦和复杂的事情。普通参数的传递方法,jobect参数的传递方法,Jni 大全
第四步:编译C
使用 gcc 编译C程序。
gcc -o libHelloC.so -fPIC -shared -I$JAVA_HOME/include -I$JAVA_HOME/include/linux Hello.c
-fPIC:告诉编译器产生与位置无关代码(Position Independent Code)
-shared:告诉编译器生成动态链接库,即共享库
JAVA_HOME/include 目录下面有 jni.h
JAVA_HOME/include/Linux 目录下面有 jni_md.h
编译结果是:在当前目录(即项目的根目录/home/Jni_example)下生成一个共享库,名字叫做libHelloC.so
第五步:运行
export 共享库,然后运行Java程序。如果忘记了export,则会出现UnsatisfiedLinkError的错误。
假设共享库的文件名是:/home/Jni_example/libHello.so
那么需要进行如下export:
export LD_LIBRARY_PATH=/home/Jni_example
值得注意的是,你可以把生成的libHelloC.so移到其它的目录(比如/usr/lib)下面去,实现共享库和源代码的分离。这样的话,你就要export LD_LIBRARY_PATH=/usr/lib。
最终运行Java程序,Caitao 和 5 是传给main方法的两个参数。java 找到 Hello.class文件,Hello.class调用libHello.so里面编译的C代码,成功运行。
java Hello Caitao 5
图:运行图片。
参考网页:参考1,参考2
讨论
讨论1:System.load()和System.loadLibrary()的区别
这两种方法都可以导入共享库(.so 文件)。假设需要导入的共享库是 /home/Jni_example/libHelloC.so,那么load和loadLibrary的区别如下:
总结一下:load是绝对路径,loadLibrary是相对路径。
讨论2:项目的java代码不直接在项目的根目录/home/Jni_example下面,而是在子目录里面
假设java文件是/home/Jni_example/com/caitao/test/Hello.java
此时 javah 应该如下操作:
cd /home/Jni_examplejavah -jni com.caitao.test.Hello
这样的话,会在项目的根目录下面生成 com_caitao_test_Hello.h 的头文件。
如果不这样做的话,会出现UnsatisfiedLinkError的错误。
讨论3:关于Makefile
步骤4中的在Linux环境下使用gcc编译C文件,也是一大学问,参考下面一个Makefile模板:
#变量定义target := mytargetsources := $(wildcard *.c) # 源代码是当前目录下面的所有C文件objects := $(sources:.c=.o)deps := $(sources:.c=.d)CC := gccRM := rm -rf#终极目标规则,一般是一个可执行文件,或者共享库$(target): $(objects) $(CC) -o $@ $^#子规则,使用静态模式规则来简化很多子规则$(objects): %.o: %.c $(CC) -o $@ -c $<#删除 .o 文件.PHONY: clean #伪目标clean: @$(RM) $(objects) $(deps)#依赖文件,必须写在最后sinclude $(deps)$(deps):%.d:%.c $(CC) -MM $< > $@
一个学习Mafefile的很不错的教程,来自实验楼(这个实验需要会员)
讨论4:异常处理
Java程序通过JNI调用C/C++的程序的时候,如果C/C++的程序发生了异常,可能导致Java程序崩溃。此时此刻需要加上异常处理机制,防止Java程序崩溃。在Native代码中捕获与抛出的案例,这个关于JNI的博客写的不错。
last update: 2017/4/5
- Jni:架起 Java 和 C/C++的桥梁,Java Native Interface
- Java 调用C/C++ , JNI(Jave Native Interface)的学习
- POI架起Java和Office之间桥梁
- JNI(Java Native Interface)的简介(从C到java)
- JNI:Java Native Interface
- JNI(java native interface)
- JNI - Java Native Interface
- JNI:Java Native Interface
- Java Native Interface (JNI)
- Java Native Interface (JNI)
- JNI:Java Native Interface
- JNI:Java Native Interface
- 轻松架起Java连接COM对象的桥梁
- 轻松架起Java连接COM对象的桥梁(续)
- 轻松架起Java连接COM对象的桥梁(续二)
- 轻松架起Java连接COM对象的桥梁
- 让POI架起Java与Office之间的桥梁
- 让POI架起Java与Office之间的桥梁
- openwrt移植到pb44---第一章(遗留问题)
- 快速搭建Spring Boot项目
- 关于eclipse没有hibernate包的问题及更改反向工程文件生成的属性类型问题
- Codeforces Round #388 (Div. 2)A Bachgold Problem
- 作用域的知识
- Jni:架起 Java 和 C/C++的桥梁,Java Native Interface
- Leetcode 72 - Edit Distance(dp)
- Android数据存储方式
- C语言实验——求三个整数的最大值 (sdut oj)
- html入门3
- 欢迎使用CSDN-markdown编辑器
- 安卓开发笔记
- Unity Mesh(三) Mesh画球
- 第十四节 Case Class与模式匹配(一)