站长原创,版权所有ITEEDU,2011-07-29
进入公司之后,由我单独负责处理ocr模块,在研究了一周之后,我觉着自己用cpp去调用tesseract-ocr接口函数,我们需呀的功能实现之后,得给他们java那边去调用呀,于是想到了java怎样才能调用我的cpp程序呢?
在tesseract-ocr安装目录中:
Bin:一些工具和命令;
Include:一些头文件;
Lib:一些库文件;
Share:配置文件和需要的语言包。
我们需要调用的接口函数就在include中,编译、运行时所需要的库就在lib中。
而tesseract-ocr源码编译所生成的.so文件有很多,我们需要一个自己编写一个调用这些接口的函数。这个函数是通过JNI机制实现的。
(1)首先编写一个.java文件,testJNI.java。
其主要功能在该类中定义一个 public native void Tesseract(int argc,String[] args)
(2)利用javac将该类编译成.class文件
(3)利用javah将.class编译成.h文件。
(4)利用c++来实现在java类中所定义的native函数,形成.cpp文件。
(5)g++和JNI机制,将.cpp编译成.o文
(6)将.o文件编译成.so文件。
这样子,我们就可以直接利用Java来调用这个.so文件了。
JNI是Java Native Interface的缩写。从Java 1.1开始,JNI标准成为java平台的一部分,它允许Java和其他语言进行交互。JNI一开始为C和C++而设计的,但是它并不妨碍你使用其他语 言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比 如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。关于 JNI 的用法很简单,有点像 java 里的 reflect 的工作机制,有兴趣的朋友可以参看Java 本地接口规范 http://linux.computersci.net/art ... pec/jniTOC.doc.html
Jni程序开发的一般操作步骤如下:
(1) 编写带有native声明的方法的java类
(2) 使用javac 或 IDE(JBuilder,eclipse等)编译所编写的java类
(3) 使用javah -jni java类名生成扩展名为h的头文件
(4) 使用C++ 实现本地方法,对调用签名可用 javap –s –p [类全名] 查看(开发 C++ 动态链接库本例是用的 VC6)
注意要从 JDK下面的 include 文件夹中把 jni.h和 jni_md.h 两个文件 copy 到你的 VC 工程里
(5) 在 Java 中 load 动态链接库文件,调用 native 方法.
或者说将项目依赖的所有原生库和资源加入到java项目的java.library.path,生成java程序。
我们测试都是在linux下边进行的,使用的是Java和C的互相调用,Java提供本地接口,C实现该本地接口。
首先,实现的是Java本地接口Hello.java,代码如下所示:
class HelloWorld { public native void sayHello(); static { System.loadLibrary("HelloWorld"); //注意在Linux下边,需要将库名的头部和尾部去掉,来加载,库名:libHelloWorld.so } public static void main(String[] args) { (new HelloWorld()).sayHello(); } }
其中,方法声明为native,其实HelloWorld类就相当于一个接口,是为其他编程语言声明的接口。System.loadLibrary("HelloWorld");语句是一个static块,也就是在该HelloWorld类加载的时候进行执行。其中,该语句实现了加载本地的动态连接库(DLL),在Linux平台下,动态连接库文件是以.so作为扩展名的,也就是标准对象(Standard Object)。
[home@administrator]# javac HelloWorld.java
接着,通过编译的HelloWorld.class文件,生成C语言的头文件,执行命令:
[home@administrator]# javah -jni HelloWorld
或者:
[home@administrator]# javah HelloWorld
但是,千万别用javah HelloWorld.class,肯定会出错,呵呵,本人就是经历了哦……
可以看到,在当前目录下生成一个HelloWorld.h文件,该文件就是C的接口文件,为使用C实现Java接口中定义的方法,可以发现在HelloWorld.h中有一个方法声明:
/* DO NOT EDIT THIS FILE - it is machine generated */ #ifndef __HelloWorld__ #define __HelloWorld__ #include <jni.h> #ifdef __cplusplus extern "C" { #endif JNIEXPORT void JNICALL Java_HelloWorld_sayHello (JNIEnv *env, jobject); #ifdef __cplusplus } #endif #endif /* __HelloWorld__ */
#include <jni.h> #include "HelloWorld.h" #include <stdio.h> JNIEXPORT void JNICALL Java_HelloWorld_sayHello (JNIEnv *env, jobject obj) { printf("Hello,the World!!!"); }
这里,方法签名为Java_HelloWorld_sayHello (JNIEnv *env, jobject obj),添加了形参obj,否则无法通过编译。
其中,#include <jni.h>
但是gcc里面默认环境可不知道jni.h是什么东西,jni.h在jdk的$JAVA_HOME/include或者$JAVA_HOME/include/linux下面,可进去查看一下。
[home@administrator]# gcc -fPIC -I jdk/include -I jdk/include/linux -shared -o libHelloWorld.so HelloWorld.c
可以在当前目录下看到libHelloWorld.so,动态连接库文件名称以lib开头。将该文件拷贝到usr/lib目录下面,就可以测试了。
[home@administrator]# java HelloWorld输出如下:
Hello,the World!!!
这只是一个非常简单的例子,主要是了解JNI在Linux下该如何用。在实际应用中,可能会非常复杂,并且要记住,一旦使用了JNI技术,系统的可移植性被破坏了。有些应用中,正是基于这种特性实现,比如限制软件的传播使用,保护开发商权益等等。