Release用Java开发的软件
这么个小问题还真是毛病多多,而且网上的文章很多都过时了,去搜索阅读浪费很多时间,所以做个笔记吧。
先说开发工具的选择,用Netbeans可以直接做出exe文件来,不过我不用它。
我用的是Eclipse, 版本3.4。这个版本已经支持RCP(Rich Client Platform)应用,所谓RCP,就是你的程序最后打包成一个小的eclipse系统,入口是个exe。RCP的缺点是会带RCP框架,做出来的可执行包基本上10M起板,太大。当然不用RCP确实也麻烦,需要用到的java/eclipse的jar包都得自己手工加入eclipse的项目路径,而这一切在RCP里能自动搞定。
所以前提到这里就说清楚了,开发工具Eclipse 3.4,图形库SWT+jface,要求编译出Windows和Linux都能用的可执行包。
其实Java并不能真的编译成本地可执行程序。有人说NativeJ可以,我试用了一下,对SWT程序不工作。
那么就只剩下把Java打包成jar包,然后挂一个外挂的可执行程序这一途。纯手工的方式是定义一个MANIFEST.MF文件,在里面定义main-class什么的,然后用Eclipse的标准菜单去export一个jar。但是MANIFEST.MF手工写太麻烦,有些清规戒律也很傻。打包成Jar包有各种各样千奇百怪的eclipse插件或者stand-alone的软件,但是到现在基本上就是FatJar笑到了最后,它已经成为Eclipse 3.5的标准配置。下载,安装FatJar, 然后export就可以很方便的做出jar包了。
假设做出的是abc.jar,那么运行下面命令启动程序。网上铺天盖地的说用javaw abc.jar
$java –jar abc.jar
通常会遇到的问题
1. 资源文件找不到
这个简单,用FatJar打包的时候要选中资源文件。在Java里面不要使用相对./这样的路径,应该使用相对class的路径。需要自己写一个GetClassPath(),然后用方法class.getClassLoader().getResourceAsStream(path)取得资源。当然了,Windows和Linux的路径分隔符号是不一样的,要注意用
String SEPARATOR = System.getProperty("file.separator");
得到路径分隔符号。
2. 拿到其它机器上出现class找不到的错误
原因是java版本。我机器上是jre1.5,目标机器是jre1.4。我遇到两个错误,一个是String类的isEmpty()方法在1.4里面没有,这个可以用length()==0来代替。
java.lang.NoSuchMethodError: java.lang.String.isEmpty()Z
另一个是java.util.concurrent.Semaphore类没有,这个只好想别的办法work around。Java真是个很折腾的语言,用Java开发就要做好让用户升级jre的觉悟。
3.拿到其它的Linux机器上出现这样的错误
ibgcj-java-placeholder.sh
This script is a placeholder for the /usr/bin/java and /usr/bin/javac
master links required by jpackage.org conventions. libgcj's
rmiregistry, rmic and jar tools are now slave symlinks to these
masters, and are managed by the alternatives(8) system.
长话短说,出现这个原因是目标机器上缺省的java环境是GNU的java,这东东是半拉子工程,现在完全不能用了。解决方法有点麻烦,需要写个脚本先 locate java,然后逐个调用java –version取得版本号,然后比较版本号大小,从当中挑最大的版本来运行自己的jar包。我本想拿sh语言写,写着写着觉得正则表达非得用awk+sed很麻烦(其实是我用得不熟),还是用了perl
#!/usr/bin/perl
# This script attempts to find an existing installation of Java that meets a minimum version
# requirement on a Linux machine. If it is successful, it will export a JAVA_HOME environment
# variable that can be used by another calling script.
$min_version = "1.5.0";
$current_version = "1.0.0";
$current_java = "";
$sToolPath = `echo $0`;
sub CompareVersion
{
my ($ver1, $ver2) = @_;
my @array1 = split( /\./, $ver1);
my @array2 = split( /\./, $ver2);
my $length = scalar @array1;
$length = scalar @array2 if( $length > scalar @array2);
for( my $i=0; $i<$length; $i++)
{
if( $array1[$i] != $array2[$i] )
{
return $array1[$i] <=> $array2[$i];
}
}
return 0;
}
sub GetTmpFile
{
mkdir("/tmp/ToolName-".$ENV{USER});
return "/tmp/ToolName-".$ENV{USER}."/tmp.var";
}
$cmds = `locate bin/java | grep java\$`;
@array = split(/\n/, $cmds);
$tmp_file = GetTmpFile();
foreach $javaexe (@array)
{
`rm -rf $tmp_file`;
system($javaexe." -version 2> ".$tmp_file." 1>/dev/null ");
$version = "";
$version = `cat $tmp_file`;
if( $version =~ /java\s+version\s+\"(.*?)\"/gsi)
{
$nVersion = $1;
$nVersion =~ s/[^0-9\.].*//;
if( (CompareVersion($nVersion, $min_version) >= 0) and (CompareVersion($nVersion, $current_version) >= 0) )
{
$current_version = $nVersion;
$current_java = $javaexe;
}
}
`rm -rf $tmp_file`;
}
if( $sToolPath =~ /(.*)\//)
{
system("$current_java -jar $1/abc.jar >/dev/null 2>&1");
}
4.让用户不需要输入java –jar来执行程序。Linux上面简单,就用上面的脚本就可以。Windows上麻烦一点,需要找个软件来做。我试用了很多,啥JPackIt,IzPack, launch4j, VAInstall等等,有一些已经被废弃了,支持新的特性不好。试用下来只有3个可用,一是exe4j,这东西是商业软件,需要买license,否则做出来的jar启动会有一个它的标志对话框跳出来。二是JShrink,很多商业软件都用它,它一来可以给class文件做混淆和加密,二来可以让文件变小一点。不过认真的人是不会相信它的加密的,另外它是商业软件需要买license。在cost down的企业打报告买生产资料是很麻烦的事情,虽然据说把它crack也不是啥难事,但我还是不高兴费那个力气了。三是JSmooth,免费好用还有人维护,那么就是它了。用法很傻瓜,不写了,over。只要一点需要补充,在Windows上,如果用了SWT,多半需要把swt-win32-3236.dll拷贝到系统可搜索路径里面去,否则可能出现找不到库的错误。
事实上这也是SWT的一大罪,调用了SWT的jar包在Linux和Windows之间是不能通用的(只调用了AWT的就可以),我想了很多办法也没解决这个问题,只好维持两个jar包,麻烦。。。
5. 出现版本异常
Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version number in .class file
at java.lang.ClassLoader.defineClass1(Native Method)
这个是编译的时候用了高版本的Java SDK, 目标机器上只有低版本的。解决方案:Project Properties –> Java Compiler –> JDK Compliance –> Complier Compliance Level –> 改成1.4
6. 用java –jar 运行,终端上出来很多SHA-Digest警告
Warning: Duplicate name in Manifest: SHA1-Digest.
Ensure that the manifest does not have duplicate entries, and
that blank lines separate individual sections in both your
manifest and in the META-INF/MANIFEST.MF entry in the jar file.
这个是fat-jar的常见问题,原因是同一个jar或者class文件在包里被包含了多次。打包前检查,删除多余的文件就可以,或者把MANIFEST.MF中除了main class以外的全删除就可以。
分类
Software评论(3)
发表评论

在速度上与native的有没有做过比较?
不用比啊,当然慢。不过所谓的GUI程序现在占CPU的比也比以前低了,所以不太看得出来,相信在3D动画会有很大差异。
Various people in the world get the loan in various banks, just because that's comfortable.