最近一直在编写各种硬件产品接口的工作,什么刷卡机,打印机,扫描仪,刻录机,其中好多是基于C++来开发,对与我来说,还是java比较顺手,于是考虑采用jni的方式来做,jni的名号,我如雷贯耳已经7,8年了,一直未真正的和他有过接触,下面把最近的一些心得总结出来。下面以我编写刻录机程序为例子:
编写java类
- package com.xk.burn;
- import java.io.InputStream;
- import org.apache.log4j.Logger;
- import com.xk.scanner.Scanner;
- public class Recorder
- {
- private static Logger logger = Logger.getLogger(Recorder.class);
- public native void burn(InputStream is);
- static
- {
- try
- {
- System.loadLibrary("xk-burn");
- }
- catch (Exception ex)
- {
- logger.error("", ex);
- }
- logger.info("load library : xk-burn sucessful!");
- }
- }
我们要调用的dll名称是xk-burn。
下面我们使用javah命令来生成c++的头文件。 打开cmd窗口,进入到编译java类生成class的目录下。执行javah com.xk.burn.Recorder 然后到这个目录下,会发现com_xk_burn_Recorder.h这个文件。下面我们打开vs2010,新建一个win32工程,然后选择dll,并且勾选空项目。然后将头文件添加进去。然后在源文件下,新建xk-burn.cpp, 代码如下:
- #include <iostream>
- #include "com_xk_burn_Recorder.h"
- JNIEXPORT void JNICALL Java_com_xk_burn_Recorder_burn(JNIEnv env, jobject jobj, jobject inputStream)
- {
- std::cout << inputStream << std::endl;
- }
这里有要注意的地方。新生成的头文件的内容为:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_xk_burn_Recorder */
- #ifndef _Included_com_xk_burn_Recorder
- #define _Included_com_xk_burn_Recorder
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_xk_burn_Recorder
- * Method: burn
- * Signature: (Ljava/io/InputStream;)V
- */
- JNIEXPORT void JNICALL Java_com_xk_burn_Recorder_burn(JNIEnv *, jobject, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
参数部分可以看到,并没有实际的变量定义,只有类型。我们必须将头文件的方法,与实现部分的方法保证一致。 上面两段代码很显然,并不一致,头文件中无参数变量,而实现中定义了明确参量变量(JNIEnv env, jobject jobj, jobject inputStream)如果我们就以目前的状态编译,然后将生成的dll,放在java工程的最外层目录下,编写测试类调用下试试,测试类如下:
- package com.xk.burn;
- import org.apache.commons.io.IOUtils;
- import com.xk.core.Initialization;
- public class RecorderTestCase
- {
- public static void main(String args[]) throws Exception
- {
- Initialization.getInitialization();
- Recorder recorder = new Recorder();
- recorder.burn(IOUtils.toInputStream("test jni"));
- System.exit(0);
- }
- }
就会抛出下面异常信息:Exception in thread "main" java.lang.UnsatisfiedLinkError: com.xk.burn.Recorder.burn(Ljava/io/InputStream;)V,这是什么原因呢,就是因为头文件和实现的参数部分不匹配,我们可以用dll 导出查看器查看一下,vc2010有dumpbin命令 我们在命令行下执行 dumpbin /exports xk-burn.dll 就可以看出,dump出心中,dll的方法名称被篡改了,所以我们的测试无法调用到方法。所以头文件应该修改为:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_xk_burn_Recorder */
- #ifndef _Included_com_xk_burn_Recorder
- #define _Included_com_xk_burn_Recorder
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_xk_burn_Recorder
- * Method: burn
- * Signature: (Ljava/io/InputStream;)V
- */
- JNIEXPORT void JNICALL Java_com_xk_burn_Recorder_burn(JNIEnv env, jobject jobj, jobject inputStream);
- #ifdef __cplusplus
- }
- #endif
- #endif
再重新编译为dll,将编译出的dll覆盖刚才java程序使用的dll,然后重新执行测试用例得到下面结果:
INFO: 2012-05-10 10:56:08,616 -- com.xk.burn.Recorder -- load library : xk-burn sucessful!
0092FCCC
至此,我们的jni的使用大功告成,可以在实现中添加我得控制刻录机的程序代码了。
本文出自 “My Joy is my joy ~” 博客,转载请与作者联系!