创建跨平台 Java SWT 应用程序 [英] Create cross platform Java SWT Application
问题描述
我使用 SWT 编写了一个 Java GUI.我使用 ANT 脚本(下面的片段)打包应用程序.
I have written a Java GUI using SWT. I package the application using an ANT script (fragment below).
<jar destfile="./build/jars/swtgui.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class" value="org.swtgui.MainGui" />
<attribute name="Class-Path" value="." />
</manifest>
<fileset dir="./build/classes" includes="**/*.class" />
<zipfileset excludes="META-INF/*.SF" src="lib/org.eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar" />
</jar>
这会生成一个 jar,在 Windows 上我只需双击即可运行我的 GUI.缺点是我必须明确地将 windows SWT 包打包到我的 jar 中.
This produces a single jar which on Windows I can just double click to run my GUI. The downside is that I have had to explicitly package the windows SWT package into my jar.
我希望能够在其他平台(主要是 Linux 和 OS X)上运行我的应用程序.最简单的方法是创建特定于平台的 jar,将适当的 SWT 文件打包到单独的 JAR 中.
I would like to be able to run my application on other platforms (primarily Linux and OS X). The simplest way to do it would be to create platform specific jars which packaged the appropriate SWT files into separate JARs.
有没有更好的方法来做到这一点?是否可以创建一个可以在多个平台上运行的 JAR?
Is there a better way to do this? Is it possible to create a single JAR which would run on multiple platforms?
推荐答案
我有一个工作实现,现在引用自 SWT 常见问题.
I have a working implementation which is now referenced from the SWT FAQ.
此方法现在可用作 ANT 任务:SWTJar
This approach is now available to use as an ANT task: SWTJar
SWTJar 现在已更新为使用 Alexey Romanov 的解决方案,如上所述.
SWTJar has now been updated to use Alexey Romanov's solution as described above.
首先,我构建了一个包含所有应用程序类的 jar.
First I build a jar containing all of my application classes.
<!-- UI (Stage 1) -->
<jarjar jarfile="./build/tmp/intrace-ui-wrapper.jar">
<fileset dir="./build/classes" includes="**/shared/*.class" />
<fileset dir="./build/classes" includes="**/client/gui/**/*.class" />
<zipfileset excludes="META-INF/*.MF" src="lib/miglayout-3.7.3.1-swt.jar"/>
</jarjar>
接下来,我构建了一个 jar 来包含以下所有内容:
Next, I build a jar to contain all of the following:
- JAR
- 我刚刚构建的罐子
- 所有的 SWT jar
- Jar-In-Jar"类加载器类
- 一个特殊的加载器类 - 见下文
这是来自 build.xml 的片段.
Here is the fragment from build.xml.
<!-- UI (Stage 2) --> <jarjar jarfile="./build/jars/intrace-ui.jar"> <manifest> <attribute name="Main-Class" value="org.intrace.client.loader.TraceClientLoader" /> <attribute name="Class-Path" value="." /> </manifest> <fileset dir="./build/classes" includes="**/client/loader/*.class" /> <fileset dir="./build/tmp" includes="intrace-ui-wrapper.jar" /> <fileset dir="./lib" includes="swt-*.jar" /> <zipfileset excludes="META-INF/*.MF" src="lib/jar-in-jar-loader.jar"/> </jarjar>
这个加载器类使用 jar-in-jar-loader 创建一个 ClassLoader,它从两个 jar 加载类.
This loader class uses the jar-in-jar-loader to create a ClassLoader which loads classes from two jars.
- 正确的 SWT jar
- 应用程序包装器 jar
一旦我们有了这个类加载器,我们就可以使用反射启动实际的应用程序 main 方法.
Once we have this classloader we can launch the actual application main method using reflection.
public class TraceClientLoader { public static void main(String[] args) throws Throwable { ClassLoader cl = getSWTClassloader(); Thread.currentThread().setContextClassLoader(cl); try { try { System.err.println("Launching InTrace UI ..."); Class<?> c = Class.forName("org.intrace.client.gui.TraceClient", true, cl); Method main = c.getMethod("main", new Class[]{args.getClass()}); main.invoke((Object)null, new Object[]{args}); } catch (InvocationTargetException ex) { if (ex.getCause() instanceof UnsatisfiedLinkError) { System.err.println("Launch failed: (UnsatisfiedLinkError)"); String arch = getArch(); if ("32".equals(arch)) { System.err.println("Try adding '-d64' to your command line arguments"); } else if ("64".equals(arch)) { System.err.println("Try adding '-d32' to your command line arguments"); } } else { throw ex; } } } catch (ClassNotFoundException ex) { System.err.println("Launch failed: Failed to find main class - org.intrace.client.gui.TraceClient"); } catch (NoSuchMethodException ex) { System.err.println("Launch failed: Failed to find main method"); } catch (InvocationTargetException ex) { Throwable th = ex.getCause(); if ((th.getMessage() != null) && th.getMessage().toLowerCase().contains("invalid thread access")) { System.err.println("Launch failed: (SWTException: Invalid thread access)"); System.err.println("Try adding '-XstartOnFirstThread' to your command line arguments"); } else { throw th; } } } private static ClassLoader getSWTClassloader() { ClassLoader parent = TraceClientLoader.class.getClassLoader(); URL.setURLStreamHandlerFactory(new RsrcURLStreamHandlerFactory(parent)); String swtFileName = getSwtJarName(); try { URL intraceFileUrl = new URL("rsrc:intrace-ui-wrapper.jar"); URL swtFileUrl = new URL("rsrc:" + swtFileName); System.err.println("Using SWT Jar: " + swtFileName); ClassLoader cl = new URLClassLoader(new URL[] {intraceFileUrl, swtFileUrl}, parent); try { // Check we can now load the SWT class Class.forName("org.eclipse.swt.widgets.Layout", true, cl); } catch (ClassNotFoundException exx) { System.err.println("Launch failed: Failed to load SWT class from jar: " + swtFileName); throw new RuntimeException(exx); } return cl; } catch (MalformedURLException exx) { throw new RuntimeException(exx); } } private static String getSwtJarName() { // Detect OS String osName = System.getProperty("os.name").toLowerCase(); String swtFileNameOsPart = osName.contains("win") ? "win" : osName .contains("mac") ? "osx" : osName.contains("linux") || osName.contains("nix") ? "linux" : ""; if ("".equals(swtFileNameOsPart)) { throw new RuntimeException("Launch failed: Unknown OS name: " + osName); } // Detect 32bit vs 64 bit String swtFileNameArchPart = getArch(); String swtFileName = "swt-" + swtFileNameOsPart + swtFileNameArchPart + "-3.6.2.jar"; return swtFileName; } private static String getArch() { // Detect 32bit vs 64 bit String jvmArch = System.getProperty("os.arch").toLowerCase(); String arch = (jvmArch.contains("64") ? "64" : "32"); return arch; } }
如上所述,对于那些寻找jar-in-jar 类加载器"的人:它包含在 Eclipse 的 JDT(基于 Eclipse 的 Java IDE)中.用存档器打开 org.eclipse.jdt.ui_*version_number*.jar ,你会在里面找到一个文件 jar-in-jar-loader.zip .我将其重命名为 jar-in-jar-loader.jar.
As stated above, for those looking for the "jar-in-jar classloader": It's included in Eclipse's JDT (the Java IDE built on Eclipse). Open org.eclipse.jdt.ui_*version_number*.jar with an archiver and you will find a file jar-in-jar-loader.zip inside. I renamed this to jar-in-jar-loader.jar.
intrace-ui.jar - 这是我使用上面描述的过程.您应该能够在 win32/64、linux32/64 和 osx32/64 中的任何一个上运行这个 jar.
intrace-ui.jar - this is the jar which I built using the process described above. You should be able to run this single jar on any of win32/64, linux32/64 and osx32/64.
现在从 SWT FAQ 中引用了这个答案.
This answer is now referenced from the SWT FAQ.
这篇关于创建跨平台 Java SWT 应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!