简单的JNI介绍和实例
LanyuanXiaoyao's Blog ヽ(✿゚▽゚)ノ

简单的JNI介绍和实例

2017-04-14
 

native关键字

native是与C++联合开发的时候用的!使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。 这些函数的实现体在DLL中,JDK的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。总而言之:

  1. native 是用做java 和其他语言(如c++)进行协作时使用的,也就是native 后的函数的实现不是用java写的。
  2. 既然都不是java,那就别管它的源代码了,我们只需要知道这个方法已经被实现即可。
  3. native的意思就是通知操作系统, 这个函数你必须给我实现,因为我要使用。 所以native关键字的函数都是操作系统实现的, java只能调用。
  4. java是跨平台的语言,既然是跨了平台,所付出的代价就是牺牲一些对底层的控制,而java要实现对底层的控制,就需要一些其他语言的帮助,这个就是native的作用了。

JNI(Java Native Interface)

native方法是通过java中的JNI实现的。JNI是Java Native Interface的 缩写。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计 的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。
目前java与dll交互的技术主要有3种:jni,jawin和jacob。jni(Java Native Interface)是sun提供的java与系统中的原生方法交互的技术(在windows\Linux系统中,实现java与native method互调)。目前只能由c/c++实现。后两个都是sourceforge上的开源项目,同时也都是基于jni技术的windows系统上的一个应用库。Jacob(Java-Com Bridge)提供了java程序调用microsoft的com对象中的方法的能力。而除了com对象外,jawin(Java/Win32 integration project)还可以win32-dll动态链接库中的方法。就功能而言:jni > jawin>jacob
Jvm封装了各种操作系统实际的差异性的同时,提供了jni技术,使得开发者可以通过java程序(代码)调用到操作系统相关的技术实现的库函数,从而与其他技术和系统交互,使用其他技术实现的系统的功能;同时其他技术和系统也可以通过jni提供的相应原生接口开调用java应用系统内部实现的功能。 在windows系统上,一般可执行的应用程序都是基于native的PE结构,windows上的jvm也是基于native结构实现的。Java应用体系都是构建于jvm之上。
jni对于应用本身来说,可以看做一个代理模式。对于开发者来说,需要使用c/c++来实现一个代理程序(jni程序)来实际操作目标原生函数,java程序中则是jvm通过加载并调用此jni程序来间接地调用目标原生函数。

JNI的使用步骤

  1. 编写带有native声明的方法的java类,生成.java文件
  2. 使用javac命令编译所编写的java类,生成.class文件
  3. 使用javah -jni java类名生成扩展名为h的头文件,也即生成.h文件
  4. 使用C/C++(或者其他编程想语言)实现本地方法,创建.h文件的实现,也就是创建.cpp文件实现.h文件中的方法
  5. 将C/C++编写的文件生成动态连接库,生成dll文件

简单的JNI实例

这是一个简单的Helloworld

1. 编写Java代码

class Helloworld{

	/*
	* native关键字修饰displayHelloworld方法,表示这个方法由native代码实现
	*/
	public native void displayHelloworld();

	/*
	* 装载动态链接库,HelloworldJNI是动态链接库名字
	*/
	static{
		System.loadLibrary("HelloworldJNI");
	}

	public static void main(String[] args) {
		Helloworld helloworld = new Helloworld();
		helloworld.displayHelloworld();	//直接调用方法
	}

}

2. 编译Java代码得到class文件

使用javac编译java文件

javac Helloworld.java


得到.class字节码文件

3. 获得JNI头文件

使用javah得到头文件

javah -jni Helloworld


得到.h头文件

头文件内容如下: 这个h文件相当于我们在java里面的接口,这里声明了一个 Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致

/* 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:    displayHelloworld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Helloworld_displayHelloworld
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

4. 编译dll文件

这里我用的是Dev C++进行编译

1. 创建dll项目


项目中自动生成的dllmain.cdll.h删去即可,因为我们需要自己编写C文件,不要它的模板

2. 编写C语言的函数实现

在项目中引入Helloworld.h头文件并写函数的具体实现

HelloworldJNI.c的代码如下:

// 引入Java生成的头文件 
#include"Helloworld.h"
#include<stdio.h>
// 引入Java的JNI支持头文件 
#include<jni.h>

// 实现Helloworld.h文件里面定义的函数 
JNIEXPORT void JNICALL Java_Helloworld_displayHelloworld(JNIEnv *, jobject)
{
	printf("Helloworld");
	return ;
}
3. 编译dll动态链接库

打开项目属性
添加如下两个头文件包含目录
需要的是其中的jni_md.hjni.h两个头文件

然后编译

得到Helloworld.dll动态链接库文件

5. 运行Java程序

把生成的Helloworld.dll文件放到Java程序目录下,然后运行Java程序

这个时候我们可以看到输出了Helloworld说明调用正确

6. 小问题

Java自动生成的.h头文件是C++的头文件,其中函数声明的写法如下

JNIEXPORT void JNICALL Java_Helloworld_displayHelloworld(JNIEnv *, jobject)

但是如果你编写的是C语言代码,那么这个函数声明的写法是会报错的,错误提示如下

错误原因是C语言中对不使用的形式参数名不可省略,在C++中,如果形式参数不使用的话,那么在函数声明的时候可以省略该参数名,所以我们如果要在C语言中使用这个头文件,就需要补全形参名,如下

JNIEXPORT void JNICALL Java_Helloworld_displayHelloworld(JNIEnv *p, jobject j)

当然,一般都写的是C++代码啦…


相似文章

下一篇 Java修饰符

评论