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 应用程序的步骤如下:
- 下载 Android SDK 和 JavaFX-Android SDK
- 创建一个 JavaFX 应用
- 使用 JavaFX-Android SDK 创建基于上述 JavaFX 应用的 Android 项目
- 使用 Ant 构建系统创建 Android 程序包
- 将程序包上传至应用商店
第一步:下载 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 代码库中的代码所创建。移植过程中有两个主要的挑战:
- 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 论坛里可以获取到更多有用的信息。
关于作者
Johan Vos从 1995 年开始使用 Java。他是帮助将 Java 移植到 Linux 的 Blackdown 小组的一员。在他合伙创建的公司 LodgON 中,他的主要工作是为社交网络软件提供基于 Java 的解决方案。由于他无法在嵌入式开发和企业应用开发之间做出选择,他的主要工作方向是端到端的 Java 开发,同时也比较精通后端系统和嵌入式设备开发。他目前最感兴趣的技术是后端的 Java EE/Glassfish 技术和前端的 JavaFX 技术。他是一个 Java Champion,是 BeJUG 和 Devoxx 指导小组成员,还是一个 JCP 成员。他是《Pro JavaFX 8》这本书的主要作者,而且他还是许多 Java 会议的演讲嘉宾(包括 JavaOne 和 Devoxx)。如果对他感兴趣,可以访问他的博客或者关注他的推特。
评论