浏览 14932 次
锁定老帖子 主题:使用JNI时,装载本地库的小技巧
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-12-23
这时,就会遇到一个问题,部署应用的时候要记住将本地库拷贝到环境变量path指定的路径中。一般在windows平台上直接copy到C:\WINDOWS\System32目录下了事。但要换一台机器部署怎么办?除了要把Java程序拿过去,还要记的把本地库也copy到正确的目录,真麻烦。于是想看看有什么好办法来解决这个问题。 首先,最容易想到的是,把本地库和class文件放在一起,利用Class.getResource(str)找到路径,然后加到环境java.library.path中: URL url = Foo.class.getResource("Foo.class");; String path = (new File(url.getPath();););.getParent();; System.setProperty("java.library.path", path);; 看上去很好,但却不能工作。查了一下ClassLoader的源代码,原来它把搜索路径定义为静态变量并只初始化一次,后面再设置java.library.path就没有用了。ClassLoader代码片断: // The paths searched for libraries static private String usr_paths[]; static private String sys_paths[]; ... if (sys_paths == null); { usr_paths = initializePath("java.library.path");; sys_paths = initializePath("sun.boot.library.path");; } 正在一筹莫展是,翻看JACOB的源代码,忽然有了惊喜的发现。 try { //Finds a stream to the dll. Change path/class if necessary InputStream inputStream = getClass();.getResource("/jacob.dll");.openStream();; //Change name if necessary File temporaryDll = File.createTempFile("jacob", ".dll");; FileOutputStream outputStream = new FileOutputStream(temporaryDll);; byte[] array = new byte[8192]; for (int i = inputStream.read(array);; i != -1; i = inputStream.read(array);); { outputStream.write(array, 0, i);; } outputStream.close();; temporaryDll.deleteOnExit();; System.load(temporaryDll.getPath(););; return true; } catch(Throwable e); { e.printStackTrace();; return false; } 高,真是好办法。和我一样,把dll放在classpath中,用Class.getResource(str).openStream()读取这个dll,然后写到temp目录中,最后用System.load(path)来动态加载。多说一句,为什么得到了jacob.dll的URL不直接去加载呢?想想看,如果把dll和class一起打成Jar包,ClassLoader还是不能加载本地库,因为System.load(path)需要的是dll的完整路径,但并不支持jar协议。还不明白,看看下面的代码: URL url = Foo.class.getResource("/java/lang/String.class");; System.out.println(url.toExternalForm(););; System.out.println(url.getFile(););; 在我的机器上的运行结果是: jar:file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class ClassLoader中用new File(name),当然会找不到文件。同时,看看我的第一种方法,就算能设置成功环境java.library.path,如果dll是在jar包中,还是加载不了。 到现在,你是不是觉得问题已经解决了?还没呢!jacob的很多源文件中已经写了下面的代码: static { System.loadLibrary("jacob");; } 除非我去掉这一句重新编译jacob的源代码,否则系统还是会报错。不过,既然有了上面的想法,稍微变通一下,就可以巧妙的解决。首先找到环境java.library.path,然后把dll拷贝到其中一个路径中就行了。 static { try { String libpath = System.getProperty("java.library.path");; if ( libpath==null || libpath.length(); == 0 ); { throw new RuntimeException("java.library.path is null");; } String path = null; StringTokenizer st = new StringTokenizer(libpath, System.getProperty("path.separator"););; if ( st.hasMoreElements(); ); { path = st.nextToken();; } else { throw new RuntimeException("can not split library path:" + libpath);; } InputStream inputStream = Foo.class.getResource("jacob.dll");.openStream();; final File dllFile = new File(new File(path);, "jacob.dll");; if (!dllFile.exists();); { FileOutputStream outputStream = new FileOutputStream(dllFile);; byte[] array = new byte[8192]; for (int i = inputStream.read(array);; i != -1; i = inputStream.read(array);); { outputStream.write(array, 0, i);; } outputStream.close();; } //dllFile.deleteOnExit();; Runtime.getRuntime();.addShutdownHook(new Thread();{ public void run(); { if ( dllFile.exists(); ); { boolean delete = dllFile.delete();; System.out.println("delete : " + delete);; } } });; } catch (Throwable e); { throw new RuntimeException("load jacob.dll error!", e);; } } 唯一的美中不足,在系统关闭的时候删除dll总是不能成功,试了两种办法都不行。想想也对,dll正被程序使用,当然不能删除。翻了一下API,Java好像没用提供unload本地库的功能,只好做罢。 解决了这么个小问题,罗罗嗦嗦一大篇,罪过罪过。后来这个项目又没有使用jacob,真对不起各位观众。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2005-12-23
好唐僧啊~
不过,方法很实用,要写个swt啥的,可以考虑啊。 lithium 写道 刚到新公司...
PS. 换公司了?能上QQ/MSN了吧。啥时候冒个泡嘛。 |
|
返回顶楼 | |