使SWT/JFace支持跨平台

由于SWT的实现机制,在不同平台下,必须引用不同swt*.jar. 由于这个瓶颈,我们要为不同的平台编译不同的版本。但是这是可以避免的。这将是本文要讨论的内容。

我一共google到了3种solution:

1,使用swtjar.jar。

http://mchr3k.github.io/swtjar/

其主页有详细的介绍。但是似乎下载链接已经无效了,一个下载的办法是从github上找到引用了它的项目,比如https://github.com/mchr3k/org.intrace/tree/3a1debcbb831f802219b341fb5e37467b365d443/org.intrace/lib

swtjar.jar的原理,似乎是通过替换掉默认的ClassLoader来实现的。

根据我的测试,使用swtjar.jar的方案,如果引用到JFace,就没办法成功load jface classes。原因我之后会讲到。

 

2, http://sourceblogger.googlecode.com/svn/trunk/multiplatform-swt-loader/src/main/java/com/github/jendap/multiplatformswt/loader/MultiPlatformSwtHelper.java

这个方案我读过代码,但是没有试过,看起来很复杂,但是似乎功能也很健全,有兴趣的可以读一下。

 

3,http://stackoverflow.com/questions/2706222/create-cross-platform-java-swt-application

这个方案最简单明了,本文主要介绍该方案

 

其实3种方案实质是一样的,把所有平台的swt*.jar都打包进程序,然后根据OS和CPU构架信息,来动态load对应的swt*.jar

 

第三种Solution

我们所要介绍的第三种solution,它的办法是,在load class阶段,不load swt*.jar。而是延迟到main函数执行阶段,再根据OS和CPU构架来”手动地”load正确的swt*.jar

1) 首先添加以下方法

private static void loadSwtJar() {

String swtFileName="";

try {

String osName = System.getProperty("os.name").toLowerCase();

String osArch = System.getProperty("os.arch").toLowerCase();

final ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();

Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);

addUrlMethod.setAccessible(true);

String swtFileNameOsPart =

osName.contains("win") ? "win32.win32" :

osName.contains("mac") ? "cocoa.macosx" :

osName.contains("linux") || osName.contains("nix") ? "gtk.linux" :

""; // throw new RuntimeException("Unknown OS name: "+osName)

String swtFileNameArchPart = osArch.contains("64") ? "x86_64" : "x86";

swtFileName = "org.eclipse.swt."+swtFileNameOsPart+"."+swtFileNameArchPart+"-4.4.jar";

URL swtFileUrl = new URL("rsrc:"+swtFileName); // I am using Jar-in-Jar class loader which understands this URL; adjust accordingly if you don‘t

addUrlMethod.invoke(parentClassLoader, swtFileUrl);

}

catch(Exception e) {

System.out.println("Unable to add the swt jar to the class path: "+swtFileName);

e.printStackTrace();

}

}

 

其作用是来根据OS和CPU构架信息,“手动地”加载正确的swt*.jar文件。

(以上代码在调试环境下可能没办法正确运行,需要在开始处添加一句:

URL.setURLStreamHandlerFactory(new RsrcURLStreamHandlerFactory(parentClassLoader));

但是在用ant编译时,则必须把这句话去掉。因为ant编译出的代码,RsrcURLStreamHandlerFactory已经被设置到URL,重复设置将出异常。其具体原因我就不深究了)

 

2) 添加jar-in-jar-loader.jar引用。

在eclipse的plugins目录下找到org.eclipse.jdt.ui_*version_number*.jar,解压它,发现jar-in-jar-loader.zip, 重命名为jar-in-jar-loader.jar。放到项目的lib目录下并引用。

需要添加这个jar的原因是,loadSwtJar方法隐式地使用了位于其中的JarRsrcLoader.class和相关的类。

 

3)在main函数最开始处添加 loadSwtJar()调用。

4)修改build.xml文件。

是的,你需要一个build.xml文件,如果没有,用eclipse的导出jar的功能生成一个。 修改build.xml文件的主要目的有2个:1是把SWT*.jar从默认加载列表中去掉,2是把所有平台的的SWT*.jar都放到打包列表中去。一加一减。给个例子:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<project default="create_run_jar" name="Create Runnable Jar for Project swtguiexample with Jar-in-Jar Loader">

<!--this file was created by Eclipse Runnable JAR Export Wizard-->

<!--ANT 1.7 is required

-->

<target name="create_run_jar">

<jar destfile="/home/binhua/Desktop/ bin /swtguiexample.jar">

<manifest>

<attribute name="Main-Class" value="org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader"/>

<attribute name="Rsrc-Main-Class" value=" swtguiexample.Main"/>

<attribute name="Class-Path" value="."/>

<attribute name="Rsrc-Class-Path" value="./ jar-in-jar-loader.jar org.eclipse.equinox.common-3.6.100.v20120522-1841.jar org.eclipse.core.commands-3.6.1.v20120521-2329.jar org.eclipse.osgi-3.8.0.v20120529-1548.jar"/>

</manifest>

<zipfileset src="lib/jar-in-jar-loader.jar"/>

<fileset dir="/home/binhua/Desktop/code/swtexample/src/swtguiexample/target/classes"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/swt/org.eclipse.swt.gtk.linux.x86_64/4.4" includes="org.eclipse.swt.gtk.linux.x86_64-4.4.jar"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/swt/org.eclipse.swt.gtk.linux.x86/4.4" includes="org.eclipse.swt.gtk.linux.x86-4.4.jar"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/swt/org.eclipse.swt.cocoa.macosx.x86_64/4.4" includes="org.eclipse.swt.cocoa.macosx.x86_64-4.4.jar"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/swt/org.eclipse.swt.win32.win32.x86_64/4.4" includes="org.eclipse.swt.win32.win32.x86_64-4.4.jar"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/swt/org.eclipse.swt.win32.win32.x86/4.4" includes="org.eclipse.swt.win32.win32.x86-4.4.jar"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/equinox/org.eclipse.equinox.common/3.6.100.v20120522-1841" includes="org.eclipse.equinox.common-3.6.100.v20120522-1841.jar"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/core/org.eclipse.core.commands/3.6.1.v20120521-2329" includes="org.eclipse.core.commands-3.6.1.v20120521-2329.jar"/>

<zipfileset dir="/home/binhua/.m2/repository/org/eclipse/osgi/org.eclipse.osgi/3.8.0.v20120529-1548" includes="org.eclipse.osgi-3.8.0.v20120529-1548.jar"/>

</jar>

</target>

</project>

以上: 1,在Rsrc<attribute name="Rsrc-Class-Path" 中,删除掉SWT*.jar,2,添加所有的平台的SWT*.jar到zipfileset节点。

 

5) 好了,用ant编译吧。

 

Trouble Shooter

以下trouble shooter事实上才是成败的关键:

#1

如果代码中引用了JFace*.jar,那么以上Solution会报ClassNotFoundException,说JFace下的某个类找不到,这是因为JFace加载失败了,为什么呢。

因为JFace*.jar还是默认加载的,JFace依赖于SWT*.jar,SWT*.jar已经延迟加载了,自然,JFace*.jar不可能加载成功。

解决办法是让JFace也延迟加载:

1,在Build.xml的Rsrc<attribute name="Rsrc-Class-Path" 中,把JFace*.jar也去掉

2,在loadSwtJar()最后一行添加

private static void loadSwtJar() {

addUrlMethod.invoke(parentClassLoader, new URL("rsrc:org.eclipse.jface-3.8.0.v20120521-2329.jar"));

}

 

2#

如果你有以下代码

public class MyApplicationWindow extends ApplicationWindow implements IExceptionHandler{

public static void main( String[] args )

{

loadSwtJar();

}

}

也会报ClassNotFoundException,为什么呢?

因为ApplicationWindow 和IExceptionHandler都是JFace下的类,main函数放在MyApplicationWindow中,要执行main函数,首先要加载ApplicationWindow 和IExceptionHandler,而这个时候,JFace还没被加载呢,记得吗,它被延迟加载了。

解决办法很简单,把main函数挪挪地方就好了。

 

3#

如果在Mac OS X下执行出错,因为必须加一个参数:

java -XstartOnFirstThread -jar *.jar

恩,不要问我为什么。

时间: 2024-08-09 08:04:13

使SWT/JFace支持跨平台的相关文章

RCP开发浅谈之SWT,JFACE

RCP开发浅谈之SWT,JFACE SWT 什么是SWT? SWT全名是Standard Widget Toolkit是一个开源的GUI编程框架,我们每一个java开发者,在学习java开发的时候都会接触到awt以及swing这两个图形库,与awt,swing两个图形库不同,swt的优势体现于底层调用本地的图形库,大大提高了运行速度(损失了一定跨平台性).SWT的一个很重要的一点,一个控件并不是单独存在的,而是存在于父控件中.这样当父控件disposed后,子控件也一定很disposed了.每一

[C++11笔记001]修改通用库中的XDynamicArray,使它可以支持C++11的初始化列表和for循环

今天,有空翻了一下<C++Primer plus(第六版)>,看到里面有介绍新的for循环和初始化列表,但是我实现的动态数组XDynamicArray不支持这些新特性,没办法,只好进行改造了. 首先是for循环,如下面的样式 for(auto e:stList) { cout<<e<<endl; } 是于就各种google,和查找C++11的array的源代码,总结:就是提供一个标准的iterator和begin,end这两个方法,就可以了. 是于定义了一个iterat

点滴记录——在Ubuntu 14.04中使SublimeText 3支持中文输入法

在Ubuntu 14.04中安装了SublimeText 3之后发现既然不支持输入中文,于是在网上搜罗一下,发现很多人遇到了同样的问题,但是解决办法大该就只有一个.下面根据自身的安装及解决办法总结如下: 1. SublimeText 3的安装 安装方式有多种,本文所描述的是从官方网站上下载64位的.deb文件 ,具体为http://c758482.r82.cf2.rackcdn.com/sublime-text_build-3059_amd64.deb文件,下载后双击即会自动使用默认的安装软件安

使Web API支持namespace

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 问题描述 假设我有一个应用场景:Core Framework可以用于任何区域的站点,其中的CustomersController有个取customer的fullname的方法GetFullName(),可想而知,这个api在中国和美国的站点上,

使安卓手机支持ipv6的终极解决方法

使安卓手机支持ipv6的终极方法 2015年3月29日 | 分类: 网络技术 测试日期:2015年3月29日 测试环境:安卓4.2 需要软件: 1,用于管理:smanager   (script manager) 2,用于编辑:Smeditor  (script manager and Editor) 3,用于浏览和寻址:ES   文件管理器 4:QQ文件传送器 5,linux文件编辑器:EDITPLUS 安装地址: C:\Documents\Tencent Files\你的QQ号码\FileR

swt jface 数据绑定

1.绑定comboviewer到对象集合: IObservableList list = BeansObservables.observeList(model, ChoiceStationModel.Property.carparkList.name()); ViewerSupport.bind(createComboViewer, list, BeanProperties.value(Carpark.Property.name.name())); 2.绑定comboviewer到基本类型集合

SWT/JFace 按键、事件、监听

JAVA.SWT/JFace: 按键与其对应的常量表.KeyEvent事件比较.VerifyEvent事件比较 2010年07月27日 星期二 22:19 <Eclipse SWT/JFACE 核心应用>清华大学出版社 8.3.2 键盘事件的各种属性 按键与其对应的常量表: 按键 keyCode常量 方向键下 SWT.ARROW_DOWN 方向键上 SWT.ARROW_UP 方向键左 SWT.ARROW_LEFT 方向键右 SWT.ARROW_RIGHT Alt键 SWT.ALT 空格键 SW

使Docker Container支持运行SWT程序

1, 下载安装JDK的docker container 我是从这个源下载已经做好的JDK8的container: https://registry.hub.docker.com/u/dockerfile/java/ docker run -it --rm dockerfile/java:oracle-java8 2, 添加GUI支持 有好几种方案,我使用的是这种: http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/

自定义python startup脚本使交互解释器支持tab和删除键

python2.7中启动python解释器需要shift+del键才能删除,不支持backspace键,不支持tab补齐.反正用起来是非常的不方便.解决这个问题可以通过指定一个python startup脚本来解决. 第一步:新增PYTHONSTARTUP的环境变量 echo "export PYTHONSTARTUP=$HOME/.pythonstartup" >> /etc/profile 第二步:创建.pythonstartup脚本 cat >> $HOM