SWT3.3 M4(2006年12月15日)新增加的功能之一是自动加载原生库,特别是从SWT的Jar文件中加载原生库的功能,大大方便了我们发布基于SWT的Java程序。SWT是怎么实现这个功能的呢? 理解其中的原理后,您也可以在您自己的程序中实现类似的功能。
SWT负责加载原生库的方法是Library类的loadLibrary (String name)
static
{
Library.loadLibrary(
"
swt
"
);
}
Library.loadLibrary()做的第一件事在库名上加上版本号,比如swt-win32-3325。
如果您的原生库文件名不包含版本号,您可以省略这一步。
String version = System.getProperty ("swt.version");
if (version == null) {
version = "" + MAJOR_VERSION;
/* Force 3 digits in minor version number */
if (MINOR_VERSION < 10) {
version += "00"; //$NON-NLS-1$
} else {
if (MINOR_VERSION < 100) version += "0";
}
version += MINOR_VERSION;
/* No "r" until first revision */
if (REVISION > 0) version += "r" + REVISION;
}
libName1 = name + "-" + Platform.PLATFORM + "-" + version;
libName2 = name + "-" + Platform.PLATFORM
第二件事是加上操作系统相关的库扩展名,比如Windows的.dll, Unix/Linux的.so.
调用System.mapLibraryName (libName1).
mappedName1
=
System.mapLibraryName (libName1);
mappedName2
=
System.mapLibraryName (libName2);
然后按顺序从swt.library.path和java.library.path指定的目录下寻找。如若没有成功,
并且没有设置swt.library.path,那么再找一下java.io.tmpdir指定的目录。要是还没有成功,Library将尝试
将SWT的Jar文件中的原生库解压到swt.library.path目录下或者java.io.tmpdir目录下。
/*
Try loading library from java library path
*/
f
(load (libName1))
return
;
if
(mapName
&&
load (libName2))
return
;
/*
Try loading library from the tmp directory if swt library path is not specified
*/
if
(path
==
null
) {
path
=
System.getProperty (
"
java.io.tmpdir
"
);
path
=
new
File (path).getAbsolutePath ();
if
(load (path
+
SEPARATOR
+
mappedName1))
return
;
if
(mapName
&&
load (path
+
SEPARATOR
+
mappedName2))
return
;
}
/*
Try extracting and loading library from jar
*/
if
(path
!=
null
) {
if
(extract (path
+
SEPARATOR
+
mappedName1, mappedName1))
return
;
if
(mapName
&&
extract (path
+
SEPARATOR
+
mappedName2, mappedName2))
return
;
}
/*
Failed to find the library
*/
throw new UnsatisfiedLinkError (
"
no
"
+
libName1
+
"
or
"
+
libName2
+
"
in swt.library.path, java.libary.path or the jar file
"
);
extract (path+SEPARATOR+mappedName1, mappedName1)将mappedName1从Jar中解压到
path+SEPARATOR+mappedName1。最后如果还是没有成功,甩出一个UnsatisfiedLinkError。
整个过程有意思的只是extract()方法,下面来看看extract
static
boolean
extract (String fileName, String mappedName)
{
FileOutputStream os
=
null
;
InputStream is
=
null
;
File file
=
new
File(fileName);
try
{
if
(
!
file.exists ())
{
is
=
Library.
class
.getResourceAsStream (
"
/
"
+
mappedName);
if
(is
!=
null
)
{
int
read;
byte
[] buffer
=
new
byte
[
4096
];
os
=
new
FileOutputStream (fileName);
while
((read
=
is.read (buffer))
!=
-
1
)
{
os.write(buffer,
0
, read);
}
os.close ();
is.close ();
if
(
!
Platform.PLATFORM.equals (
"
win32
"
))
{
try
{
Runtime.getRuntime ().exec (
new
String []
{
"
chmod
"
,
"
755
"
, fileName}
).waitFor();
}
catch
(Throwable e)
{}
}
if
(load (fileName))
return
true
;
}
}
}
catch
(Throwable e)
{
try
{
if
(os
!=
null
) os.close ();
}
catch
(IOException e1)
{}
try
{
if
(is
!=
null
) is.close ();
}
catch
(IOException e1)
{}
}
if
(file.exists ()) file.delete ();
return
false
;
}
Library.class.getResourceAsStream ("/" + mappedName)返回指向Jar根目录下mappedName文件的
InputStream.个人感觉,extract写得有点乱,if套着if,if套着try,当然并不是很糟糕,本身这是一个比较简单的函数。
在我自己的程序中,重写了extract(改名为extractAndLoad),相对来说要清爽一些,请大家比较。
通过上面的分析,我们可以很简单实现我们自己的loadLibrary
import
org.eclipse.swt.SWT;
import
java.io.File;
import
java.io.FileOutputStream;
import
java.io.InputStream;
/**
* The method loadLibrary load a native library in the order
* from java.library.path, os.library.path, tmpdir and from jar.
*
*
@author
pan
*/
public
class
Library {
static
final
String SEPARATOR
=
System.getProperty(
"
file.separator
"
);
public
static
void
loadLibrary(String name) {
//
Try loading library from os library path
String path
=
System.getProperty(
"
os.library.path
"
);
if
(path
!=
null
) {
path
=
new
File(path).getAbsolutePath();
if
(_load(System.mapLibraryName(path
+
SEPARATOR
+
name)))
return
;
}
//
Try loading library from java library path
if
(_load(name))
return
;
//
Try loading library from the tmp directory if os library path is not specified
if
(path
==
null
) {
path
=
System.getProperty(
"
java.io.tmpdir
"
);
path
=
new
File(path).getAbsolutePath();
if
(_load(System.mapLibraryName(path
+
SEPARATOR
+
name)))
return
;
}
//
Try extracting and loading library from jar
if
(path
!=
null
&&
loadFromJar(System.mapLibraryName(path
+
SEPARATOR
+
name),
System.mapLibraryName(name)))
return
;
//
Failed to find the library
throw
new
UnsatisfiedLinkError(
"
no
"
+
name
+
"
in java.libary.path, os.library.path or jar file
"
);
}
private
static
boolean
_load(String libName) {
try
{
if
(libName.indexOf(SEPARATOR)
!=
-
1
) {
System.load(libName);
}
else
{
System.loadLibrary(libName);
}
return
true
;
}
catch
(UnsatisfiedLinkError e) {
}
return
false
;
}
private
static
boolean
loadFromJar(String outFileName, String libName) {
try
{
return
extractAndLoad(outFileName, libName);
}
catch
(Throwable e) {
}
return
false
;
}
private
static
boolean
extractAndLoad(String outFileName, String libName)
throws
Throwable {
int
read;
byte
[] buf
=
new
byte
[
4096
];
InputStream is
=
null
;
FileOutputStream os
=
null
;
File file
=
new
File(outFileName);
if
(file.exists())
file.delete();
if
(
!
file.exists()) {
try
{
os
=
new
FileOutputStream(file);
is
=
Library.
class
.getResourceAsStream(
"
/
"
+
libName);
if
(is
==
null
||
os
==
null
)
return
false
;
while
((read
=
is.read(buf))
!=
-
1
)
os.write(buf,
0
, read);
}
finally
{
if
(os
!=
null
)
os.close();
if
(is
!=
null
)
is.close();
}
if
(
!
SWT.getPlatform().equals(
"
win32
"
)) {
Runtime.getRuntime().exec(
new
String[] {
"
chmod
"
,
"
755
"
, outFileName }).waitFor();
}
return
_load(outFileName);
}
return
false
;
}
}
然后把您的原生库打在Jar里面,用Library.loadLibrary加载原生库,而不是System.loadLibrary,这样您就
把原生库隐藏在您的Jar里面了。
转载请保留
http://www.blogjava.net/xilaile/archive/2007/03/23/105706.html