为什么要用JNI?因为有些功能JAVA无法提供,比如对扫描仪驱动,我现在就是要搞这个,网上给的例子都是SB.我气不过,便要自己去搞.感觉很悲剧.搜来想去,只能想办法通过C/C++来操作,然后用JAVA去调用C.这就需要JNI了.
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++).这是百度百科上说的.通俗来说,就是JAVA调用C/C++函数的接口.如果你要想调用C系列的函数,你就必须遵守这样的约定.
就一个native的关键字.
public class NativeDemo {
{
/**
* 系统加载其他的语言的函数
*/
System.load("C:\\Users\\Administrator\\Desktop\\com\\Hello.dll");
}
/**
* 就这个natice关键字.标记了这个接口,看起来像是abstract
*/
public native void sayHello();
public static void main(String[] args) {
new NativeDemo().sayHello();
}
}
静态代码块先不讲。先看native方法。
sayHello()方法加了一个关键字native,就代表是一个native接口.执行这个方法时,会根据jni.h来找到真正的C来编写的sayHello()的实际函数.
它实际上就存在%JAVA_HOME%\bin\include下面的一个文件,另外还有个%JAVA_HOME%\bin\include\win32下的jni_md.h.
这东西不说其他的作用,我也不清楚,只知道它里面存储了大量的函数和对象,它有个很好的方法就是通过native接口名,获取C函数.
打个比方类似如下:
public static String getCMethod(String javaMethodName);
它可以根据你的java接口,找到C函数并调用.
但这就意味着,你不能在C里随意写函数名,因为如果你写的java方法叫native aaa();C函数也叫aaa();但jni.h通过getCMethod(String javaMethodName)去找的结果是xxx();那这样就无法调用了.
既然不能随意写,怎么办?
没事,jdk提供了一个通过java方法生成c函数接口名的工具javah.
就像java是运行main方法一样,javah就是提供具有native method的java对象的c函数接口.
dos命令如下:
javac NativeDemo.java
javah NativeDemo
这个命令可以提供一个c函数的接口.
上面那个NativeDemo被javah了之后就生成了一个文件Hello.h(可能我改了名字),就是C函数的接口.里面有方法名和返回值什么的.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class NativeDemo */
#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeDemo
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeDemo_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
最重要的C函数接口就是这样:JNIEXPORT void JNICALL Java_NativeDemo_sayHello(JNIEnv *, jobject);
JNIEXPORT :在Jni编程中所有本地语言实现Jni接口的方法前面都有一个"JNIEXPORT",这个可以看做是Jni的一个标志,至今为止没发现它有什么特殊的用处。
void :这个学过编程的人都知道,当然是方法的返回值了。
JNICALL :这个可以理解为Jni 和Call两个部分,和起来的意思就是 Jni调用XXX(后面的XXX就是JAVA的方法名)。
Java_NativeDemo_sayHello:这个就是被上一步中被调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。
JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)
jobject obj:代表着native方法的调用者,本例即new NativeDemo();但如果native是静态的,那就是NativeDemo.class .
也就是说,我们的native sayHello()方法实际上是运行C的Java_NativeDemo_sayHello()这个方法,我们是不能随意写C函数名的的,只能这样写。
接下来我们可以照着接口去写真正的函数方法了.新建Hello.cpp
/* Replace "dll.h" with the name of your header */
#include "Hello.h"
#include
#include
JNIEXPORT void JNICALL Java_NativeDemo_sayHello(JNIEnv *, jobject){
using namespace std;
cout << "Hello___________World";
}
这个方法什么都没有做,就打印了一句话"Hello___________World"
然后将Hello.h和Hello.cpp编译运行出dll文件.生成Hello.dll
这个过程中可以能编译会出现问题.
说“jni.h”: No such file or directory:
你需要把jdk里面的那俩jni.h给拷贝过来,再编译,如果还出错.需要把Hello.h里的头部#include
这样你现在就拥有了下列文件
实际上你现在就只需要NativeDemo和Hello.dll就行了。
别急,还有一个。NativeDemo里的静态代码块请准确把dll库给load进去
System.load("C:\\Users\\Administrator\\Desktop\\com\\Hello.dll");
然后编译java,运行
C:\Users\Administrator\Desktop\com>javac NativeDemo.java
C:\Users\Administrator\Desktop\com>java NativeDemo
Hello___________World
C:\Users\Administrator\Desktop\com>
非常完美。C++里的打印被调用了。