Java 不适合编写桌面应用,这是事实还是偏见?
本文作者以个人视角对 Java 桌面发展历程做了回顾,内容来自他在上世纪九十年代后期担任 Java 开发者时的所见所感,主要讲述曾经的“杀手级”桌面语言 Java 是为何从 21 世纪开始颓势尽显、步入衰落的。值得一提的是,作者如今在做一款开发者友好型 Java 桌面部署工具(jDeploy),其实他还是希望 Java 可以重拾风采,再度变得对桌面开发具有吸引力。
我的大学时代正好赶上 2000 年之前的最后那几年,当时 Java 是计算科学专业里的官方语言。虽然也有几门课程要求使用 C 和 C++,但所有基础知识教学都是围绕 Java 语言来设计的。为了防止有读者朋友较真,我再说得更准确些:这些课程会用 Java 语言来教学,不是说只教 Java。
其实大学的计算机科学课并不教授特定的编程语言,教学内容主要是相关概念,再使用时下流行的编程语言来演示这些概念,所以学生们得拿出课余时间掌握编程语言。也有些课程要求更严格,比如“这份作业必须使用 Java 完成”,但大多数高级课程就不再做此限制,大家想用什么语言就用什么语言。(有些作业需要使用特定的库或者编程语言。我还记得有一份作业就是用 pthreads 库为操作系统分配线程,相当于强制使用 C 语言。)
我上大学那会,对 Java 的认知还仅限于 Applet。我既不清楚 Java 在行业里是什么地位,也不知道它跟其他编程语言相比到底有什么优势。而且越是深入学习,我就越觉得 Java 这东西有点名不副实——它更像是种玩具语言,毕竟那些“真正”的严肃开发都是用 C++这类语言实现的。反正系里总有一帮故作深沉、头发胡子一大把的家伙,总爱吹嘘跟 C++一比、Java 屁都不是。
Java 很慢
他们倒也不是胡搅蛮缠,最常见的理由就是“Java 很慢”。相信任何用过 Java GUI 应用程序或者包含 Java Applet 网页的朋友都同意这个观点。那时候用 Java 编写的桌面应用程序就只有开发工具,我能想起的就是 ArgoUML 和 NetBeans,它们确实不好用而且速度很慢。那种慢,就像是双脚陷进了泥潭——无论是上下滚动还是打开菜单,所有操作都有“粘粘”的延迟。
但支持 Java 的教授们则坚持认为,只要配合即时编译,Java 也是可以快起来的。而且在编译了代码路径之后,“理论上”它的运行速度可以追平甚至超越 C++。但我们这帮学生根本不买账,单纯觉得他们是在嘴硬。
Java 应用程序不是“真正的”应用程序
另一个让人感觉 Java“不上档次”的原因,在于我们开发的应用程序都不是本机应用程序。Java 构建的应用程序只是一堆.class 文件的集合;哪怕再“高阶”一点,生成的.jar 文件也只能在安装了 Java 的计算机上运行。相比之下,其他学校的朋友们展示的项目就洋气多了,这些可是货真价实的可执行文件——双击之后,它们就像真正的专业应用程序那样开跑,有程序容器、也有屏幕顶端菜单,这才像话嘛。
我记得自己问过一位教授关于 Java 能不能生成本机可执行文件,他的回答是“为什么非得这样?生成本机可执行文件,Java 的跨平台优势不就没了吗?”
如果真的想把应用程序部署成桌面程序,他建议我研究研究 Java WebStart。这样不用本机程序包,Mac 和 Windows 用户也能顺利安装我的应用程序。WebStart 听起来挺有搞头,但我还是觉得跟自己的真正目标不太相符。毕竟就算可以用 WebStart 分发应用程序,用户也仍然需要事先安装 Java。我承认,当时已经是 2001 年,大多数计算机都预装了 Java。但跟直接双击就能打开,这种体验仍然不够好。
另外,在亲自尝试了一些 WebStart 应用程序之后,我发现它的表现也就那样。应用程序的打开速度还是慢,因为启动后需要先下载更新;这些程序也没有被正确集成到操作系统当中。虽然 WebStart 也提供在桌面上为应用程序保存执行别名的功能,但效果不好。
我相信那时候肯定已经有第三方工具能把 Java 应用程序成本机可执行文件,但不光会占用大量资源、而且绝对“只支持 Windows”。我也关注过 GCJ,这款 Java GNU 编译器宣称能把 Java 编译成机器码。但它只适用于 API 子集而且不支持 Swing——所以用户就只有两个选项,要么使用本机 GUI 工具包、要么干脆不要 GUI。
所以我心里有了答案:至少在当时,Java 桌面开发已经是死路一条,唯一的用处就是写点小程序——问题是跟 Flash 这类更轻、更快的技术相比,Java Applet 的优势其实也已经不明显了。
Applets:杀手级应用
虽然我这份“编年史”没那么详尽,但要谈 Java 在桌面端的发展历程,杀手级应用 Applets 肯定是个绕不开的话题。
Applets 在 1995 年那会确实颇具开创性,它们首次让用户在网页之内看到了交互式的 2D 图形与动画。最初(Java 1.0 时代),Java 解释器是被内置在浏览器当中的;但不久之后,就改为通过插件调用系统中已经安装的 Java 运行时。
最早的小程序嵌入起来非常简单,直接把.jar 或者.class 文件上传到 Web 服务器、再向网页中添加<applet>标记就行。遗憾的是,这种便捷性很快就消失了。随着 Java 新版本的发布和 IE 浏览器的出现,嵌入小程序所需要的 HTML 代码越来越复杂,需要针对不同的浏览器和 Java 版本使用不同的标签。虽然<applet>标签号称可以在“多浏览器”环境下正常嵌入小程序,但 IE 上实际使用的却是<object>标签,而 Mozilla 上使用的则是<embed>标签。
于是之前的嵌入方法
<APPLET code="MyAppletClass.class" archive="Applet.jar, EJB.jar" width="600" height="500" > </APPLET>
就慢慢变成了:
<OBJECT classid="clsid: 8AD9C840-044E-11D1-B3E9-00805F499D93"
width="600" height="500">
<PARAM NAME=CODE VALUE=MyAppletClass.class>
<PARAM NAME="archive" VALUE='Applet.jar, EJB.jar'>
<PARAM TYPE="application/x-java-applet;version=1.5.0">
<PARAM NAME="scriptable" VALUE="false">
<PARAM NAME="cache-option" VALUE="Plugin">
<PARAM NAME="cache-archive" VALUE="Applet.jar, EJB.jar">
<COMMENT>
<EMBED type="application/x-java-applet;version=1.5.0" CODE=MyAppletClass.class
ARCHIVE="Applet.jar, EJB.jar" WIDTH="600" HEIGHT="500"
scriptable="false">
<NOEMBED>
</COMMENT>
</NOEMBED>WebSphere Java Application/Applet Thin Client for
Windows is required.
</EMBED>
</OBJECT>
如果系统上还没安装 Java,那情况就复杂了,几乎没有比较体面的处理方法。因为嵌入代码是由 NetBeans 生成的,所以小程序的构建过程相当复杂、需要由 JavaScript 检测系统中是否安装有 Java。如果没有,则提供指向 Sun 网站的 Java 下载链接。
直到 Java 1.3 版本,小程序的用户体验都非常糟糕,以至于 Applet 只能在系统管理员完全可控的客户端软件环境中才能使用。于是乎,靠 Java Applet 在网页中添加简单交互的计划基本破产。
时间快进到 2001 年,小程序的生命基本走到了终点。Flash 凭借着更简、更轻便、更快捷,取代 Java Applet 成为浏览器媒体交互的业界标准,也获得了更广泛的安装基础(我记得当时 99%的计算机都安装了 Flash)。
不止如此,小程序还大大败坏了 Java 的名声,其中很多安全漏洞都被宣传成“Java 漏洞”。这就给人留下一种错误印象,即任何用 Java 编写的东西都是潜在的安全威胁——虽然实际上这些“漏洞”只是小程序自己的问题。尽管之后很多年仍时不时能看到 Java 小程序的身影,但它在人们心中早已成了过时技术的代名词。
我一直觉得 Applets 算不上可行的桌面应用程序发布方式,但它能经久不衰、肯定也有自己的独到之处。
GUI 工具包:AWT、Swing 与 SWT
我刚开始使用 Java 那会,它的初始 GUI 工具包 AWT(Abstract Windowing Toolkit)已经有点过时了,倒是新的“轻量级”工具包 Swing 得到了人们的青睐。简单来讲,AWT 属于“重量级”工具包,提供的是 用于处理本机小部件的 API。重量级 UI 库的问题在于难以维护,而且受到底层平台可用组件的限制。相比之下,Swing 则拥有轻量化优势,能够绘制自己的一组小部件、降低了维护难度,帮助用户轻松绘制出自己的跨平台界面。
Swing 提供可插入 UI,支持样式设置以模拟本机平台的外观。所以在 Mac 上运行时,Swing UI 的观感与 Cocoa 等本机应用程序完全相同;而在 Windows 上运行时,观感又高度接近 Windows。此外,Swing 还允许自定义外观,让程序的使用体验脱离任何操作系统平台。总之,这是一款灵活的 UI 解决方案。
但在 2000 年初的计算机上,Swing 界面也是出了名的资源杀手。我自己有一台半透明蓝色的 iMac,它搭载的 233 MHz 芯片几乎运行不了 NetBeans。我爸爸的 G4 电脑搭载 400 MHz 处理器,性能倒是更强一些,但整个运行体验仍然勉勉强强、凑凑合合。
所以在当时,用 Java 构建 GUI 要求人们对摩尔定律抱有极大的信心——虽然当下的运行表现不好,但再过几年应该会有起色。
时间来到 2002 年,一天室友向我推荐了 Eclipse 与 SWT——这是一套似乎能够解决性能问题的 Java GUI 开发替代方案。Eclipse 使用的是 SWT(Standard Widget Toolkit),一款新的“重量级”Java UI 工具包,但响应速度明显要比使用 Swing 进行构建的 NetBeans 更快。所以乍看之下,长久的难题似乎终于有了答案。
SWT 的优势在于无需自行绘制小部件,而仅仅是为了平台的本机小部件提供绑定,因此由它构建的应用程序在观感上原生度更高、响应速度也更快。但经历过 AWT 的糟糕体验,我仍然保持着警惕。既然 Sun 公司的聪明人都觉得轻量化才是正确的道路,为什么 IBM 这边拿出的是重量级工具包呢?
而且我对 SWT 的兴奋也没持续多久。Eclipse 虽然比 NetBeans 响应更快,但用起来仍然有种笨拙的感觉,完全达不到本机应用的水平。倒是 Swing,虽然速度还是更慢,但一直随着新版本的发布而不断改善。根据 AWT 与 Swing 相关书籍、论坛和博文的数量,我估计 Swing 社区的规模比 SWT 大得多。Swing/AWT 曾经是、现在也仍然是 Java 中内置的唯一工具包,能够确保开发者无需任何第三方依赖项、单凭 Java 运行时环境就构建起完整的 GUI 应用程序。
虽然我还没有在项目中实际使用过 SWT,但很高兴看到它能经受住这么多年的风雨考验。期间先后出现过不少不支持 Swing 的 JVM(Avian 就是其中一种精简型 AOT(预先)编译器,它不支持 Swing、但提供使用 SWT 的 GUI 演示),靠的就是 SWT 这个能在 Java 平台上快速编写 GUI 应用程序的解决方案。
据我所知,2000 年初那会的跨平台 Java GUI 开发市场就是由 AWT、Swing 和 SWT 这三家主导。Java FX 直到 2007 年才出现。
Java Cocoa 应用
还是在 2000 年初,苹果突然宣布要把 Java 作为 Mac OS X 上的首选编程语言。Java 被预装在 OS X 当中,Swing 也获得了本机 Mac 主题,使其观感高度接近于本机应用程序。这意味着大家完全可以将 Java 应用程序直接发布给 Mac 用户,代码一定能在机器上运行起来、而且提供与本机系统相匹配的观感体验。
他们还推出能将 Java 应用程序打包成本机 OS X.app 的工具,所以开发者就能把 Java 应用程序像真正的本机应用那样交付给用户。如果认真遵循 Mac 用户界面指南,使用者甚至感受不到这款应用程序是用 Java 编写的。
遗憾的是,大多数 Swing 应用程序的开发者并没有遵循 Mac UI 指南,所以用户在使用 Java 应用程序还是能感觉到事情“不太对劲”。比如应用程序可能在菜单项中使用了错误的加速键、甚至不提供标准菜单。没错,虽然听起来很简单,但想让 Swing UI 在 Mac 上完全适配本机风格还是颇有难度。这里我们用 Mac UI 的本机工具包 Cocoa 来对比:Cocoa 提供的是完全原生的应用程序外壳,并且以菜单为起点;但 Swing 应用程序则是从零开始。开发者必须自行创建窗口和菜单,除非直接套用框架——但我从没见过能纯原生 Mac 应用程序体验的 Java 框架。
但苹果总有办法,他们更进一步、为 Coca 提供了 Java 绑定包。如此一来,我们的 Java 应用程序不仅看起来更像是本机应用程序,实际上也成了本机应用程序。我们只需要在 Xcode 中创建一个新的 Cocoa 项目,再选择 Java 作为项目语言即可。它会为大家提供漂亮的本机应用程序外壳作为设计起点,而在按下“Build”键时,生成的将是一个可以直接发送给用户的纯本机应用程序。
我也用 Cocoa 试着编写过示例应用程序,效果非常完美。但即便表现出色,它在行业中也仍然不受待见。因为这类应用程序只适用于 Mac,毕竟用的是 Mac 上的专有 UI,所以 Java 社区里“一次编写、随处运行(WORA)”的狂热支持者们对 Cocoa 嗤之以鼻。另一方面,“原生”Mac 开发社区则对 Java 不感兴趣,所以相关文档很少。而且要实现从 Cocoa 到 Java 的对接,开发者必须能熟练地将 Objective-C 代码转换成 Java 中的等价表示——相当累人。
所以不出所料,苹果在几年之后的 2005 年就放弃了 Cocoa-Java 项目。而且出于种种原因,苹果对 Java 的兴趣也很快淡去。我记得当时史蒂夫·乔布斯还有句名言,“Java 如同开发者的镣铐”。他说得一点儿没错。
如果大家也想试试用 Java 编写 Cocoa 应用程序,请关注Rococoa项目。作为 Cocoa-Java 理想的继任者,它目前仍处于活跃状态。
这让我们何去何从
我的这份“编年史”既不全面、也不完全遵照时间顺序。我讲述的是自己在 Java 桌面环境上的真实经历,而且主要偏向 Mac 一侧(因为家里的第一台计算机是苹果 IIGS,我爸后来又买了台 Mac Classic)。
所以结合个人经历,2005 年可以说是 Java 语言在桌面环境中的发展转折点。在 2005 年之前,网络论坛上有着大量关于 Java 桌面技术的问答内容,例如 Swing、Cocoa Bridge 等。但到 2005 年之后,相关内容快速减少。那 2005 年前后到底发生了什么重大转变?Java 桌面开发者们又跑到哪里去了?我猜大部分开发者可能转向了服务器端,而继续坚守客户端的开发者也许是转向了 Web 或者本地开发方面。