使SWT/JFace支持跨平臺

因爲SWT的實現機制,在不一樣平臺下,必須引用不一樣swt*.jar. 因爲這個瓶頸,咱們要爲不一樣的平臺編譯不一樣的版本。可是這是能夠避免的。這將是本文要討論的內容。java

我一共google到了3種solution:linux

1,使用swtjar.jar。git

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

其主頁有詳細的介紹。可是彷佛下載連接已經無效了,一個下載的辦法是從github上找到引用了它的項目,好比https://github.com/mchr3k/org.intrace/tree/3a1debcbb831f802219b341fb5e37467b365d443/org.intrace/libmacos

swtjar.jar的原理,彷佛是經過替換掉默認的ClassLoader來實現的。app

根據個人測試,使用swtjar.jar的方案,若是引用到JFace,就沒辦法成功load jface classes。緣由我以後會講到。eclipse

 

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

這個方案我讀過代碼,可是沒有試過,看起來很複雜,可是彷佛功能也很健全,有興趣的能夠讀一下。函數

 

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 Shooting

如下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

恩,不要問我爲何。

相關文章
相關標籤/搜索