用JavaFX構建部署Android應用

InfoQ發表於2014-09-29

Java平臺最初的目標是為嵌入式裝置提供一個軟體環境。然而,歷史的怪圈卻讓Java成為了企業軟體開發的首選語言。過去,Java的客戶端應用所受到的關注比利潤豐厚的伺服器端市場要少得多。不過,現在Java平臺已經擁有了強大的客戶端元件——JavaFX,可用於開發桌面、平板電腦、移動和嵌入式系統上的應用程式。本文將為讀者展示如何在Android裝置上部署JavaFX應用程式。

任何致力於客戶端開發的軟體平臺都需要有一套建立使用者介面的方法。AWT(抽象視窗工具包)曾經被看作是Java平臺使用者介面的根基。一些更高階的工具包(例如Swing)在一定程度上都是以AWT為基礎的。自從1995年Java首次釋出,AWT就是Java平臺的一部分,現在看來,其設計原則已經相當陳舊,無法與當今的硬體和軟體能力相匹配。

新的Java客戶端元件,JavaFX,是在充分汲取了Java領域以及其他UI框架的經驗後,重新設計而成。JavaFX的關鍵原則之一就是要儘可能地充分利用硬體(如GPU)資源。實際上,如今的使用者介面所需的工具包必須是高響應並且高效能的。

JavaFX作為官方的Java客戶端元件,是Java SE環境重要的組成部分。在所支援的系統上,它是與JDK和JRE繫結在一起的。因此,在Windows,MacOS X,Linux和嵌入式ARM系統上,Oracle將JavaFX作為Java SE版本的一部分統一發布。對於iOS和Android平臺來說,目前尚無Oracle官方釋出的JavaFX。不過,開發者社群已經彌補了這一缺口。RoboVM團隊正在新增RoboVM對JavaFX的支援,這樣就可以使用RoboVM編譯器編譯JavaFX應用程式並在iOS裝置上執行它們。

能夠讓JavaFX應用執行在iOS和Android平臺上是至關重要的。如今,越來越多的應用程式不僅需要能夠在桌面電腦上執行,也需要能夠在移動裝置和平板電腦上執行。用三種語言編寫同一應用的三種版本的代價相當昂貴:一個桌面版本,一個iOS版本和一個Android版本。而使用JavaFX,則可以將同一個應用直接部署到全部三個平臺上。當然,各個平臺都有其各自的UI特性需要遵循,不過JavaFX平臺提供了許多方法以達成這一目標,例如使用CSS和自定義皮膚。

稍後在本文中我們將著重闡述要能夠在iOS和Android平臺執行JavaFX應用的另一個原因。在此之前,我們首先為大家介紹如何在Android平臺上執行已有的JavaFX應用。

Android上的JavaFX

本文的剩餘部分將介紹如何在Android上部署JavaFX應用程式。關於如何在Android平臺上編譯、打包和部署JavaFX應用程式的詳細說明可以訪問JavaFX移植團隊的網站

通常來說,部署JavaFX應用程式的步驟如下:

  1. 下載Android SDK和JavaFX-Android SDK
  2. 建立一個JavaFX應用
  3. 使用JavaFX-Android SDK建立基於上述JavaFX應用的Android專案
  4. 使用Ant構建系統建立Android程式包
  5. 將程式包上傳至應用商店

第一步:下載Android SDK和JavaFX-Android SDK

在編譯和構建應用程式之前,首先需要安裝Android SDK和JavaFX-Android SDK。

Android SDK是由Google提供的軟體開發工具包,可以從Android開發者支援網站上下載。其中包含了android.jar API和將Java類檔案轉換成Dalvik位元組碼的工具。Android SDK還提供了可與Android裝置通訊的工具,用於日誌檢查和將應用程式傳輸到裝置上。下載完成後,可以很方便地將ANDROID_SDK環境變數指向所下載的adt-bundle-xxx/sdk資料夾(xxx與所下載的版本和對應的作業系統相關。)

Dalvik JavaFX-Android SDK(由JavaFX的Android移植團隊提供)可以從BitBucket網站的JavaFX Ports專案中下載。下載並解壓最新的dalvik-sdk-version.zip檔案。(我們可以將環境變數DALVIK_SDK設定指向剛剛解壓縮的dalvik-sdk資料夾)。JavaFX-Android SDK中包含一個執行在Android平臺上的JavaFX實現,一些用於構建Android程式包的工具以及一個“Hello,Android” JavaFX示例程式。可以在剛剛下載的DALVIK_SDK/samples目錄下找到這個示例程式。除此之外,我們還需要用於構建apk檔案的Ant程式。如果電腦上還沒有Ant程式,可以從Apache Ant網站上下載。

第二步:建立JavaFX應用

在Android平臺建立JavaFX應用與在桌面電腦系統上建立JavaFX應用的步驟完全一致。我們可以選擇慣用的IDE和構建工具來建立JavaFX應用。這樣,建立Android程式包的旅途就順利開始了。在這一步,我們無須建立特定的JavaFX應用程式啟動器或增加任何配置。JavaFX-Android SDK已經為我們提供了相應的工具,我們將在接下來的步驟裡討論這些工具。

雖然通常來說,最好能夠保持程式碼的平臺獨立性,不過在某些情況下,例如在沒有相應的JavaFX或Java API可用時,如果能夠利用Android平臺特有的實現會更加方便一些。Android平臺提供了許多為應用提供各種功能的服務(如獲取位置資訊)。要使用這些服務,只需新增對android.jar(位於Android SDK中)的依賴並引用其所包含的類即可。不過,需要注意的是,這些Android平臺特有的功能無法在其他系統上(如桌面系統)使用。另外,考慮到桌面應用的UI面積比手持裝置的更大,在建立Android應用的佈局時,需要能夠適用於較小的UI面積。可以研究一下samples資料夾下的HelloAndroid類,以瞭解如何獲取螢幕的邊界並使用螢幕邊界設定Stage和Scene的邊界。

如果想要用示例程式執行上述步驟,使用cd命令切換到之前下載的DALVIK_SDK目錄下的samples/HelloWorld資料夾下。

將應用打包成jar檔案。打包示例程式,首先需要cd切換到DALVIK_SDK/samples/HelloWorld/javafx目錄下,然後構建應用程式——在Linux和MacOS上使用./gradlew,在Windows上使用gradlew.bat。(我們不需要下載Gradle,gradlew會為我們完成這項工作。)執行這個命令會在javafx/build/libs資料夾下建立一個jar檔案(HelloWorld.jar)。除了Gradle之外,我們也可以使用Maven,Ant或者其他任何工具來構建應用,只要能夠生成jar檔案即可。

第三步:使用JavaFX-Android SDK生成基於JavaFX應用的Android專案

DALVIK_SDK目錄下的“tools”資料夾中包含一個名為“convertJavaFXToAndroid.sh”的構建指令碼,可用於生成Android專案。簡單來說,Android專案就是一個資料夾,其中包含了一些檔案和構建指令碼,用於生成Android程式包(apk檔案)。

構建專案之前,我們需要進行一系列的引數配置,包括Android SDK和JavaFX-Android SDK的位置,JavaFX應用的位置以及包含main類的檔名稱等。

我們只需對DALVIK_SDK/samples/HelloWorld/convertJavaFXToAndroid.sh檔案稍作修改,就能夠讓它正常執行。在檔案頂部,將變數ANDROID_SDK指向之前下載的ANDROID_SDK資料夾。(目前還沒有Windows版本的構建指令碼convertJavaFXToAndroid.bat,不過我們可以拷貝.sh檔案,然後稍作修改,就可以自制一個Windows版本的構建指令碼。)關於生成Android專案的更多資訊,請參見DALVIK_SDK/samples/HelloWorld/README檔案。

在呼叫convertJavaFXToAndroid.sh指令碼之前,在這個指令碼的同一資料夾下必須要包含build.gradle及其他gradle相關的檔案。將samples目錄下的所有gradle相關的檔案和整個gradle目錄拷貝到我們自己建立的JavaFX專案的根目錄下,並對convertJavaFXToAndroid.sh指令碼做出相應的修改。然後呼叫convertJavaFXToAndroid.sh指令碼。我們就可以在samples/HelloWorld/javafx/build目錄下找到新生成的Android專案。

在構建專案時,我們可以使用與示例程式的構建指令碼相同的模式。相關引數的定義如下:

-PDIR:生成Android專案的資料夾路徑。
-PPACKAGE:Android包名稱。
-PNAME:生成的Android apk檔名。
-PANDROID_SDK:Android SDK。
-PJFX_SDK:Dalvik SDK。
-PJFX_APP:JavaFX應用jar包所在目錄。
-PJFX_MAIN:JavaFX主啟動器的類名。

第四步:使用Ant構建系統建立Android程式包

在上一步驟中生成的Android專案資料夾“build”中包含一個build.xml檔案。切換到build資料夾下並執行“ant debug”。這個命令會生成一個可以安裝到Android裝置上的Android“除錯程式包”。

使用Android SDK中的Android工具可以將上述.apk檔案傳送到裝置上。如,呼叫命令

$ANDROID_SDK/platform-tools/adb -r install /path/to/the.apk

如果需要獲取日誌資訊,可以執行如下命令:

$ANDROID_SDK/platform-tools/adb logcat

完美的組合——JavaFX應用和應用商店

需要讓JavaFX應用能夠執行在iOS和Android平臺的另一個重要的原因是通過應用商店(Apple應用商店和Google Play商店)釋出的這種模式。過去,如何將Java應用釋出到各種各樣的移動手機上,是令許多Java客戶端開發人員感到相當有挫敗感的工作之一。儘管有過許多標準化的嘗試(例如,MIDP),不過如果沒有裝置製造商、網路運營商或二者共同的幫助,想要將J2ME應用部署到大量的裝置上仍是一件相當困難的事情。現在,在移動裝置上建立和安裝應用已經變得十分簡單。iOS和Android平臺都有自己的應用商店,可供開發者上傳應用,終端使用者下載和執行應用。雖然iOS應用商店和Android Play商店都有一系列應用程式需要滿足的要求和設計準則,不過在iOS應用商店和Google Play商店都已經存在JavaFX的應用,也就是說基於JavaFX的應用可以被應用商店所接受。這為Java客戶端應用開發者創造了巨大的市場。

JavaFX的Android應用與任何其他Android應用沒有什麼兩樣。上傳到Google Play商店的方式是相同的。而且與其他Android應用類似,在上傳應用之前,需要將許多指導方針考慮在內。這些指導方針不應被視為令人厭煩的教條,而應被看作是提供給JavaFX開發者的幫助,以保持他們的應用與其他Android應用的一致性,這會讓終端使用者更容易接受JavaFX應用。

第五步:部署到應用商店(正在進行的工作)

將JavaFX應用上傳到Google Play商店並非十分困難,不過目前為止仍有許多步驟需要遵循。從使用慣用的IDE編寫JavaFX應用到將應用提交到Play商店的道路仍然很漫長。而且需要熟悉各種各樣的系統。如果用Maven開發JavaFX應用,就需要用到3種不同的構建系統:用於應用開發的Maven,用於建立Android專案的Gradle和用於構建Android程式包的Ant。

意圖改善這個問題的一些工作正在進行中。有許多方法能夠改變這種狀況,其中一些方法已在研究階段。例如, Android最近將Gradle轉為其首選的構建環境。Android的Gradle外掛可以讓編譯JavaFX應用變得更加容易,不過目前仍然還有許多問題有待解決。

另外,與JDK一同釋出的JavaFXPackager主要用於為不同的目標環境提供相應的程式包。如果這一工具能夠整合在JavaFX-Android SDK中將會更好。

此外,一些IDE(NetBeans、Eclipse和IntelliJ IDEA)已經包含了一些用於Android開發的外掛。如果能夠在這些IDE中整合對JavaFX的支援,熟悉這些IDE的開發人員的工作將會變得更加輕鬆高效。

JavaFX的Android移植團隊選擇先為開發者提供一個端到端工具集,以幫助他們將應用上傳到Play商店中。這說明已經沒有技術或法律問題阻礙我們編寫Android上的JavaFX應用。

接下來,端到端部署的各個部分也將會不斷完善。

底層實現揭祕

JavaFX平臺是在OpenJDK的子專案——OpenJFX中開發的,OpenJDK通常被視為Java平臺開發的大本營。所有的程式碼開發和話題討論都是基於一個開放的環境的。

OpenJFX程式碼庫中包含了多個平臺上的JavaFX API和實現。JavaFX本身的體系結構是模組化的,平臺相關的部分與通用的部分是相互獨立的。考慮到JavaFX其中一項設計原則就是要儘可能的利用硬體加速,能夠實現這樣的設計相當不易。

JavaFX-Android SDK是基於OpenJFX程式碼庫中的程式碼所建立。移植過程中有兩個主要的挑戰:

  1. OpenJFX程式碼中包含一些原生程式碼,需要交叉編譯這些程式碼以適用於Android系統。
  2. Android上的Dalvik執行環境只包含Java 7的一個子集。不過對拉姆達表示式的支援已經包含在其中。

第一個挑戰已經在OpenJFX中徹底解決。實現Android平臺上的JavaFX原生功能所需的所有程式碼已經包含在OpenJFX中。

第二個挑戰之所以能夠解決的主要原因是OpenJFX的開發者們已經達成共識,不在JavaFX 8u20這一版本中使用Java 8特有的功能。不過,還有許多對Java 7的API呼叫仍然無法在Android平臺上正常執行。好訊息是JavaFX-Android SDK本身已經包含了這些缺失的API實現。RetroLambda 專案(已經包含在釋出包中)能夠替換類檔案中動態呼叫的位元組碼,因此我們可以在Android平臺上的JavaFX應用中使用拉姆達表示式。另外,需要注意的是,目前還不支援java.util.Streams。

Android概念的對映

Android應用的生命週期與典型的桌面應用的生命週期有一定區別。Android使用了Activity的概念。Android與JavaFX之間的概念轉換作為其中一部分工作,由JavaFX-Android SDK統一完成。JavaFX-Android SDK包含了一個名為FXActivity的Activity的子類,在JavaFX應用啟動時,這個類將被例項化。

整個JavaFX應用都是這個Activity的一部分。在JavaFX應用啟動時,JavaFX將接管一切。這樣做的好處是讓JavaFX的生命週期事件和應用的組織結構,包括導航,能夠像桌面應用一樣。在進行生命週期管理時,不需要了解Android平臺相關的知識。

一般情況下,開發者希望應用是裝置無關的,不過在很多時候,也希望能夠充分利用Android的特性。針對這些情況,JavaFX-Android SDK為JavaFX開發者提供了一些用於訪問Android API的鉤子。第一個鉤子就是由JavaFX-Android SDK生成的Android的清單檔案。預設的清單檔案適用於比較簡單的應用,不過開發者也可以對其進行配置。在這個檔案中將完成許可權請求和基礎的Android配置引數的設定工作。

另外,Android平臺還為JavaFX平臺提供了許多與Android裝置關聯更加緊密的服務,例如位置服務、與NFC閱讀器通訊的服務等。在標準的Android應用中,可以通過Android的Activity和Context類訪問這些服務。在JavaFX平臺中並不存在這些Android特有的類。不過JavaFX-Android SDK提供了一個靜態方法

Context context = FXActivity.getInstance();

用來訪問與建立和執行JavaFX應用的Activity相關聯的Context例項。

這個靜態方法所返回的Context例項可用於獲取Android特有的服務。關於如何使用NFC閱讀器的示例可以參考這裡。在開源專案OpenMapFX中可以找到另外一個類似的關於如何使用GPS服務的示例。

如有更多關於JavaFX在Android平臺上的問題,從Google小組中的JavaFX Android論壇裡可以獲取到更多有用的資訊。

相關文章