JNI开发流程及HelloWorld

JNI全称是Java Native Interface(Java本地接口)单词首字母的缩写,本地接口就是指用C和C++开发的接口。由于JNI是 JVM规范中的一部份,因此可以将我们写的JNI程序在任何实现了JNI规范的Java虚拟机中运行。同时,这个特性使我们可以复用以前用C/C++写的大量代码。

开发JNI程序会受到系统环境的限制,因为用C/C++语言写出来的代码或模块,编译过程当中要依赖当前操作系统环境所提供的一些库函数,并和本地库链接在一起。而且编译后生成的二进制代码只能在本地操作系统环境下运行,因为不同的操作系统环境,有自己的本地库和CPU指令集,而且各个平台对标准C/C++的规范和标准库函数实现方式也有所区别。这就造成使用了JNI接口的JAVA程序,不再像以前那样自由的跨平台。如果要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相应的动态库。、

JNI开发流程主要分为以下6步:

1、编写Java源代码;

2、将Java源代码编译成class字节码文件;

3、用javah -jni命令生成.h头文件(javah是jdk自带的一个命令,-jni参数表示将class中用native声明的函数生成jni规则的函数);

4、用本地代码实现.h头文件中的函数;

5、将本地代码编译成动态库 (linux/unix: *.so);

6、拷贝动态库至  java.library.path 目录或设置本地库搜索目录,并运行Java程序。

通过上面的介绍,相信大家对JNI及开发流程有了一个整体的认识,下面通过一个HelloWorld的示例,再深入了解JNI开发的各个环节及注意事项。

PS:本人的开发环境为Ubuntu14,如果在其它操作系统下开发也是一样,只需将本地代码编译成当前操作系统所支持的动态库即可。

这个案例用命令行的方式介绍开发流程,这样大家对JNI开发流程的印象会更加深刻。

第一步、并新建一个HelloWorld.java源文件,内容如下:
public class HelloWorld{
	
	static {
		System.loadLibrary("HelloWorld");  // 2.加载实现了native函数的动态库,只需要写动态库的名字;
	}
	
	public static void main(String args[]){
		new HelloWorld().print();  // 3.调用本地函数;
	}
	
	public native void print();  // 1.声明这是一个native函数,由本地代码实现;
}
第二步、用javac命令将.java源文件编译成.class字节码文件:
javac HelloWorld.java

执行完此命令就会在当前目录下生产HelloWorld.class文件;

第三步、用javah -jni命令,根据class字节码文件生成.h头文件( -jni参数是可选的 ):
javah -jni HelloWorld

默认生成的.h头文件名为:HelloWorld.h(类名.h),也可以通过-o参数指定生成头文件名称;

HelloWorld.h文件的内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_print
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
第四步、用本地代码实现.h头文件中的函数创建HelloWorld.c文件:

HelloWorld.c文件内容如下:

#include <jni.h>
#include <stdio.h>

JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject clazz)
{
	printf("Hello, World!\n");  //打印信息
}

第五步、将C/C++代码编译成本地动态库文件:

动态库文件名命名规则: lib+动态库文件名+后缀(操作系统不一样,后缀名也不一样)如:

Linux/Unix:libHelloWorld.so

gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared HelloWorld.c -o libHelloWorld.so

参数说明:

-I:           包含编译JNI必要的头文件;

-fPIC:     编译成与位置无关的独立代码;

-shared: 编译成动态库;

-o:         指定编译后动态库生成的路径和文件名;

第六步、运行Java程序:

Java在调用native(本地)方法之前,需要先加载动态库。如果在未加载动态之前就调用native方法,会抛出找不到动态链接库文件的异常。如下所示:

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.study.jnilearn.HelloWorld.sayHello(Ljava/lang/String;)Ljava/lang/String;
	at com.study.jnilearn.HelloWorld.sayHello(Native Method)
	at com.study.jnilearn.HelloWorld.main(HelloWorld.java:9)

一般在类的静态(static)代码块中加载动态库最合适,因为在创建类的实例时,类会被ClassLoader先加载到虚拟机,随后立马调用类的static静态代码块。这时再去调用native方法就万无一失了。加载动态库的方式:

System.loadLibrary("HelloWorld");

只需要指定动态库的名字即可,不需要加lib前缀,也不要加.so后缀。

如果使用上述方式,java会去java.library.path系统属性指定的目录下查找动态库文件,如果没有找到会抛出java.lang.UnsatisfiedLinkError异常。

Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloWorld2 in java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
	at java.lang.Runtime.loadLibrary0(Runtime.java:845)
	at java.lang.System.loadLibrary(System.java:1084)
	at com.study.jnilearn.HelloWorld.<clinit>(HelloWorld.java:13)

有两种方式可以让java从java.library.path找到动态链接库文件,聪明的你应该已经想到了。

方式1:将动态链接库拷贝到java.library.path目录下;

方式2:给jvm添加“-Djava.library.path=动态链接库搜索目录”参数,指定系统属性java.library.path的值

可是使用命令:export LD_LIBRARY_PATH=. 

再次执行java HelloWorld命令,输出如下:

Hello, World!

完毕!

转自:http://blog.csdn.net/xyang81/article/details/41777471

转载于:https://my.oschina.net/cht2000/blog/914553

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值