原文地址:http://electrofriends.com/articles/jni/jni-part1-java-native-interface/
JNI简介,JNI的意义以及特点
- JNI指定了Java代码和本地代码之间的通信协议。
- JNI使得Java代码可以和使用不同语言(如C/C++等)编写的本地代码进行交互。
- 本地代码通常可以直接访问CPU和寄存器,所以它们比解释代码(如JAVA)执行得更快。
- Java代码中的一些方法有时需要在Java中声明使用,但却要通过别的代码来实现。
JNI可以让程序员做这些事情
- 通过控制硬件,使用一些平台特性,不再局限在JVM当中。
- 使用本地已经提供的代码库而不用重复发明轮子。
JNI的缺点
- 不再是平台无关。
- 代码健壮性存在一定问题。当Java代码通过JNI调用的本地代码存在内存泄漏或者是空指针等问题时,在Java代码中很难进行调试。
JNI在系统中的位置
- JNI作为JVM实现的一部分,JNI是一个本地代码和Java代码之间的双向接口。它使得不同的代码可以互相调用。
- 作为一个双向接口,JNI支持两种类型的本地代码:native libraries 和 native applications。
HelloWorld
下面通过实例介绍如何通过JNI让Java代码去调用一个简单的C程序。
主要包括以下几个步骤:
- 编写Java类,在类中声明一个本地方法,并在main()方法中调用它。
- 编译.java文件,生成.class文件。
- 使用javah工具生成C代码的.h头文件,并在.c文件中实现.h头文件声明的方法。
- 把C程序编译成.dll动态链接库文件。
- 调用HelloWorld.class
步骤一 编写Java类:
/**
* HelloWorld.java
**/
class HelloWorld {
public native void print(); //native method
static //static initializer code
{
System.loadLibrary("CLibHelloWorld");
}
public static void main(String[] args)
{
HelloWorld hw = new HelloWorld();
hw.print();
}
}
在这里要注意两个地方:
1.首先是使用native关键字声明了一个本地方法。
public native void print();
这是在告诉编译器print()方法是一个本地方法,它的实现应该在外部库中去找。
2.然后是在static块中加载了动态链接库
static //static initializer code
{
System.loadLibrary("CLibHelloWorld");
}
被static围起来的代码块将在调用print()方法之前被执行,也就是系统将会把包含了print()方法的具体实现的动态链接库事先加载进来。
步骤二 编译.java文件
javac HelloWorld.java
得到HelloWorld.class类
步骤三 使用javah工具生成C代码的.h头文件
javah –jni HelloWorld
生成的头文件为HelloWorld.h,它是这样子的:
/**
* HelloWorld.h
**/
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* 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
注意这一行:
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
这里声明了一个返回值为void,名为Java_HelloWorld_print的C函数。
第一个参数是JNI运行环境的指针。
第二个存数是HelloWorld对象的引用。
然后我们来编写一个本地的C程序,如下:
#include "HelloWorld.h"
#include "jni.h"
#include "stdio.h"
JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello world\n");
return;
}
代码实现了.h头文件中声明的函数。很简单地打印出”Hello world\n”。
步骤四 把C程序编译成.dll动态链接库文件
Part2. 使用Visual Studio把C代码编译成.dll文件
步骤五 调用HelloWorld.class
如果一切顺利,将会看到控制台打印出Hello world。
当Java代码无法找到.dll或者方法名不对时,会报错java.lang.UnsatisfiedLinkError。