将JNI的dll添加到jar包并发布和执行的方法

时间:2022-05-13 19:59:40

原文链接:http://blog.csdn.net/caesarzou/article/details/5672760

参考英文链接:http://frommyplayground.com/how-to-load-native-jni-library-from-jar/

经常会有这种情况,需要用JNI来实现某些java不支持的操作(比如windows上操作智能卡),同时又对此操作实现一定的java层面的封装,并需要发布给第三方开发者进行使用。一般的处理方式是,提供一个jar包,同时提供一个dll,并要求第三方开发者将此dll拷贝到C:/windows/system32目录下。这种操作方式不够傻瓜,最爽的方式当然是和纯java一样,提供一个jar包就全部解决。

 

首先,Java加载JNI的动态库,有两种方式:

    System.load(String filename);  //指定加载的dll全路径文件名

    System.LoadLibrary(String dllname);  //指定加载的dll名(无path,无后缀)

 

很明显Load是绝对路径,LoadLibrary的方式和windows加载dll的方式一样,按当前目录和path系统目录的顺序查找文件。

但这两种显然都不适合动态的部署。因为每个系统的环境变量可能并不完全相同。所以好的方式,是在运行态的时候确定。

思路是:dll在jar包中,也就是属于类的资源,所以可以在类初始化的时候,从Resource里面获得。然后拷贝为一个临时文件,由System.load指定全路径进行加载。并利用临时文件的属性在退出的时候删除。

如此实现:

public class Test{
 static {
  try {
  InputStream in = Switch.class.getClass().getResource("/NTV.dll").openStream();
  File dll = File.createTempFile("NTV", ".dll");
  FileOutputStream out = new FileOutputStream(dll);

  int i;
  byte [] buf = new byte[1024];
  while((i=in.read(buf))!=-1) {
   out.write(buf,0,i);
  }
  
  in.close();
  out.close();
  dll.deleteOnExit();

  System.load(dll.toString());// 
  }catch (Exception e) {
   System.err.println("load jni error!");
  }
 }
 public native int func(String str);

 public static void main(String[] args) {

    func("hello");

 }

}

 

在eclipse的项目目录上建立dll目录,将NTV.dll拷贝进来,并将dll目录添加到ClassPath。即能进行调试。在调试OK后,建立ant工程

<project name="native_method" default="jarop" basedir=".">
 <target name="build_javah">

 <delete dir="bin" />
  <mkdir dir="bin"/>

  <javac scrdir="src" dstdir="bin"/>
  <javah destdir="jni" classpath="bin" force="yes">
   <class name="Test"/>
  </javah>
 </target>
 <target name="jarop">
  <jar destfile="Test.jar" >
   <fileset dir="bin" includes="**/*.class" />
   <fileset dir="dll" includes="NTV.dll" />
   <manifest>
    <attribute name="Class-Path" value="."/>
    <attribute name="Main-Class" value="Test"/>
   </manifest>
  </jar>
 </target>
</project>

 

此时dll被添加到jar包的根上,尝试着在任何位置 java -jar */Test.jar 都可以正确的执行。