在 Mac 上正确运行基于 SWT 的跨平台 jar

2022-09-01 20:48:43

我一直在研究一个基于SWT的项目,该项目旨在部署为Java Web Start,因此可以在多个平台上使用。

到目前为止,我已经设法解决了由于SWT依赖的系统特定库而产生的导出问题(请参阅相关线程)。生成的jar似乎在32/64位linux和64位Windows上工作正常,但是在Mac上执行失败,并具有以下输出:

$ java -jar dist/test.jar 
Adding { file:/Volumes/LaCie/ChiBE_Local/swt/swt-cocoa-macosx-x86_64-3.6.1.jar } to the classpath
***WARNING: Display must be created on main thread due to Cocoa restrictions.
Exception in thread "main" java.lang.reflect.InvocationTargetException
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: java.lang.ExceptionInInitializerError
   at org.eclipse.gef.tools.MarqueeSelectionTool.<init>(MarqueeSelectionTool.java:99)
   at org.gvt.MarqueeZoomTool.<init>(MarqueeZoomTool.java:16)
   at org.gvt.action.MarqueeZoomToolAction$1.<init>(MarqueeZoomToolAction.java:28)
   at org.gvt.action.MarqueeZoomToolAction.createTool(MarqueeZoomToolAction.java:28)
   at org.gvt.action.AbstractGEFToolAction.<init>(AbstractGEFToolAction.java:24)
   at org.gvt.action.MarqueeZoomToolAction.<init>(MarqueeZoomToolAction.java:20)
   at org.gvt.TopMenuBar.createBarMenu(TopMenuBar.java:113)
   at org.gvt.ChisioMain.createMenuManager(ChisioMain.java:617)
   at org.eclipse.jface.window.ApplicationWindow.addMenuBar(ApplicationWindow.java:235)
   at org.gvt.ChisioMain.main(ChisioMain.java:149)
   at org.gvt.RuntimeMain.main(RuntimeMain.java:14)
   ... 5 more
Caused by: org.eclipse.swt.SWTException: Invalid thread access
   at org.eclipse.swt.SWT.error(Unknown Source)
   at org.eclipse.swt.SWT.error(Unknown Source)
   at org.eclipse.swt.SWT.error(Unknown Source)
   at org.eclipse.swt.widgets.Display.error(Unknown Source)
   at org.eclipse.swt.widgets.Display.createDisplay(Unknown Source)
   at org.eclipse.swt.widgets.Display.create(Unknown Source)
   at org.eclipse.swt.graphics.Device.<init>(Unknown Source)
   at org.eclipse.swt.widgets.Display.<init>(Unknown Source)
   at org.eclipse.swt.widgets.Display.<init>(Unknown Source)
   at org.eclipse.swt.widgets.Display.getDefault(Unknown Source)
   at org.eclipse.swt.widgets.Display$1.run(Unknown Source)
   at org.eclipse.swt.graphics.Device.getDevice(Unknown Source)
   at org.eclipse.swt.graphics.Resource.<init>(Unknown Source)
   at org.eclipse.swt.graphics.Cursor.<init>(Unknown Source)
   at org.eclipse.draw2d.Cursors.<clinit>(Cursors.java:170)
   ... 16 more

我已经检查了许多相关的线程:(无法在Mac OS X上获得SWT显示在Mac上使用SWT的问题)以及SWT FAQ和教程中的UI线程条目,例如将Java应用程序带到Mac在Mac OSX上部署SWT应用程序

我的理解是,问题源于Mac OSX上的线程处理,我应该尝试在执行时实现JVM参数。这是对的吗?-XstartOnFirstThread

假设我对这个问题的理解是准确的,我有点困惑,因为这个软件是跨平台的,并在javaws上运行。我是否需要创建一个文件,如果是这样,在包中的位置以及如何创建,否则我如何在执行时“有条件地”将该参数传递给JVM?info.plist

提前致谢,


答案 1

是的,您肯定需要在Mac OS X上完成这项工作。由于它是一个 VM 参数,因此只能在启动应用程序时指定它,因此无法从代码中检测操作系统并在它是 Mac OS X 时进行设置。Eclipse站点上的解决方案创建了一个合适的Mac OS X My Application.app,这是特定于平台的,同样,在您的情况下是不可行的。-XstartOnFirstThread

但是,我只是尝试在Windows XP上运行一个Eclipse RCP应用程序,并指定了参数,它根本没有抱怨。这意味着您可以在 JNLP 文件中指定此参数,并且假定它将在所有其他平台上被忽略,并在 Mac OS X 上被选取。-XstartOnFirstThread

更新:如果由于某种原因在任何平台上造成麻烦,或者您只想做正确的事情,那么还有另一种可能的解决方案。您可以在浏览器中检测用户的操作系统(假设应用程序是从网页启动的),并为 Mac OS X 和其他平台提供不同的 JNLP。-XstartOnFirstThread

更新 2:正如注释中所指出的,有一个关于使用 Java Web Start 部署 SWT 应用程序的教程。我只是在Mac OS X(10.6.x)上启动了JNLP,它就工作了。在查看示例 JNPL 时,我发现了以下内容:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+"
    codebase="http://www.eclipse.org/swt/jws/"
    href="controlexample.jnlp">
<information>
      <title>Control Example</title>
      <vendor>eclipse.org</vendor>
      <homepage href="http://www.eclipse.org/swt/jws/" />
      <description>A demonstration of SWT Widgets</description>
      <description>Control Example</description>
</information>

<security>
    <all-permissions />
</security>

<resources>
    <extension href="swt.jnlp"/>
    <jar href="controlexample.jar" />
</resources>

<application-desc main-class="org.eclipse.swt.examples.controlexample.ControlExample" />
</jnlp>

请注意末尾的行,指向特定于平台的 SWT JNLP 文件(此处省略了某些部分):<extension href="swt.jnlp"/>

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+"
    codebase="http://www.eclipse.org/swt/jws/"
    href="swt.jnlp">
<information>
      <title>SWT</title>
      <vendor>eclipse.org</vendor>
      <homepage href="http://www.eclipse.org/swt/jws/" />
      <description>SWT</description>
</information>

<security>
    <all-permissions />
</security>

<resources os="Windows" arch="x86">
    <j2se version="1.4+" />
    <jar href="swt-win32-windows-x86.jar" />
</resources>

...

<resources os="Mac\ OS\ X">
    <j2se version="1.5*" java-vm-args="-XstartOnFirstThread"/>
    <jar href="swt-carbon-osx-universal.jar" />
</resources>

<component-desc/>
</jnlp>

在文件末尾:Mac OS X特定参数。-XstartOnFirstThread


答案 2

SWT(像任何其他UI框架一样)有一个“UI线程”。这通常是主线程(即执行的线程)。对 UI 方法的所有调用都必须在此线程中发生。main(String[] args)

如果需要从非 UI 线程调用 UI 方法,则必须将其包装:

Display.getDefault().asyncExec( new Runnable() { 
    public void run() {
         //ui call here
    }
} );

如果需要等待结果,可以使用syncExec()