Java平台最初的目标是为嵌入式设备提供一个软件环境。然而,历史的怪圈却让Java成为了企业软件开发的首选语言。过去,Java的客户端应用所受到的关注比利润丰厚的服务器端市场要少得多。不过,现在Java平台已经拥有了强大的客户端组件——JavaFX,可用于开发桌面、平板电脑、移动和嵌入式系统上的应用程序。本文将为读者展示如何在Android设备上部署JavaFX应用程序。任何致力于客户端开发的软件平台都需要有一套创建用户界面的方法。AWT(抽象窗口工具包)曾经被看作是Java平台用户界面的根基。一些更高级的工具包(例如Swing)在一定程度上都是以AWT为基础的。自从1995年Java首次发布,AWT就是Java平台的一部分,现在看来,其设计原则已经相当陈旧,无法与当今的硬件和软件能力相匹配。新的Java客户端组件,JavaFX,是在充分汲取了Java领域以及其他UI框架的经验后,重新设计而成。JavaFX的关键原则之一就是要尽可能地充分利用硬件(如GPU)资源。实际上,如今的用户界面所需的工具包必须是高响应并且高性能的。能够让JavaFX应用运行在iOS和Android平台上是至关重要的。如今,越来越多的应用程序不仅需要能够在桌面电脑上运行,也需要能够在移动设备和平板电脑上运行。用三种语言编写同一应用的三种版本的代价相当昂贵:一个桌面版本,一个iOS版本和一个Android版本。而使用JavaFX,则可以将同一个应用直接部署到全部三个平台上。当然,各个平台都有其各自的UI特性需要遵循,不过JavaFX平台提供了许多方法以达成这一目标,例如使用CSS和自定义皮肤。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设备上运行它们。稍后在本文中我们将着重阐述要能够在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应用能够运行在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代码库中的代码所创建。移植过程中有两个主要的挑战:
OpenJFX代码中包含一些原生代码,需要交叉编译这些代码以适用于Android系统。
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论坛里可以获取到更多有用的信息。
更多干货笔记关注微信公众号 : 老九学堂
来源:oschina
链接:https://my.oschina.net/u/4300698/blog/3382275