在一些Java应用中需要实用JNI来调用DLL动态链接库,但是,如果做成模块打成JAR包,一般情况是无法直接载入DLL的。
常用的DLL载入方式如下
- //加载test.dll
- System.loadLibrary("test");
- System.load("test");
需要说明的是加载DLL的有效路径与一个JVM中的系统变量有关,可以用以下方法查看该变量。
- System.getProperty("java.library.path");
在该变量中含有当前路径".",需要特别说明,这个当前路径是执行时的当前路径,举例来说。
- //当前路径在C:根目录下,而执行的程序在C:/app/Test.class
- C:\> java app/Test //这时的 . 表示的是C:\
- C:\app> java Test //这时的 . 表示的是C:\app
接下来就是重点了,DLL已经打包到了JAR中,比如打进了APP.jar,那对于Windows来说,这个DLL不存在于任何的目录中,自然无法正常调用到。那解决方法就是在执行的时候将JAR包中的DLL解到某个地方,然后再载入。因为使用下面的代码可以将JAR包中的任何文件作为资源载入。
- MyClass.class.getResourceAsStream(dllName); MyClass.class.getResource(dllName);
这样就可以在JAR包执行时把DLL读取出来解在一个地方了,我写了一个方法来代替loadLibrary,DLL会被放入系统的临时文件夹中,在那里会被载入,synchronized保证该过程在多线程下安全,也许在将来可以优化。
- //BIN_LIB为JAR包中存放DLL的路径
- //getResourceAsStream以JAR中根路径为开始点
- private synchronized static void loadLib(String libName) throws IOException {
- String systemType = System.getProperty("os.name");
- String libExtension = (systemType.toLowerCase().indexOf("win")!=-1) ? ".dll" : ".so";
- String libFullName = libName + libExtension;
- String nativeTempDir = System.getProperty("java.io.tmpdir");
- InputStream in = null;
- BufferedInputStream reader = null;
- FileOutputStream writer = null;
- File extractedLibFile = new File(nativeTempDir+File.separator+libFullName);
- if(!extractedLibFile.exists()){
- try {
- in = SMAgent.class.getResourceAsStream(BIN_LIB + libFullName);
- if(in==null)
- in = SMAgent.class.getResourceAsStream(libFullName);
- SMAgent.class.getResource(libFullName);
- reader = new BufferedInputStream(in);
- writer = new FileOutputStream(extractedLibFile);
- byte[] buffer = new byte[1024];
- while (reader.read(buffer) > 0){
- writer.write(buffer);
- buffer = new byte[1024];
- }
- } catch (IOException e){
- e.printStackTrace();
- } finally {
- if(in!=null)
- in.close();
- if(writer!=null)
- writer.close();
- }
- }
- System.load(extractedLibFile.toString());
- }