即使你是一名经验丰富的J2SE/J2EE开发人员,如果你想成为一名好的移动Java开发人员,你就必须理解移动设备、无线网络和移动用户各自的特性。本文解释了移动应用程序开发中面临的一些挑战,以及如何克服这些困难。
J2ME允许桌面或企业Java开发者把自己已有的经验技巧迁移到为企业和顾客建立智能移动应用程序上面。这些技巧包括Java语言的基本概念、API和公共设计模式。但是,从桌面、服务器或瘦客户端领域盲目地进行“技巧迁移”带来的缺陷可能比优点还要多。例如,大多数运行在PersonalJava和J2ME个人环境(Personal Profile)上的基于AWT的J2SE应用程序不经过修改,直接迁移到移动设备上常常导致难以接受的性能和很差的可用性。为了建立成功的智能移动应用程序,开发者必须理解移动设备和网络的具体特性。
作为Java开发者和架构人员,我们应该了解移动开发的一些什么样内容?我们如何再次训练自己以适应新的事务?我们分析了移动应用程序开发中的主要挑战,并讨论了克服这些困难的最佳经验。
有限的设备硬件
移动平台和PC平台之间最明显的差异是用于处理的硬件。比起任何移动处理设备,目前的PC都拥有更快的CPU、更快的内存和更多的存储空间。桌面和服务器开发者可以负担编写“浮肿的”的应用程序(例如微软Office)的大量开销,他们还能够访问大型的、集成了大量功能的框架组件(例如J2SE平台本身)提供的丰富的用于提高效率的特性。但是,在移动设备上就完全不同了。有时CPU速度还不到20MHz、内存小于100KB,我们必须仔细地评估自己所需要的特性,彻底地优化代码,在有限的框架组件支持下工作。在这一部分我们将讨论这些挑战。
1、轻量级的类库
初学者最普遍的错误是“golden hammer”反模式(anti-pattern):为事务选择了错误的技术。在Java的世界中,软件工具通常作为标准的或第三方类库中的可重复使用的对象存在。选择占用最少的硬件资源、支持必要的应用程序特性的最好的类库是我们工作的要点。
J2ME基础和个人环境(也包括PersonalJava)在字节码(bytecode)层次与J2SE是兼容的,并且它继承了J2SE核心API的一个很大的子集。在理论上,我们可以把J2SE程序库(例如XML处理、加密、消息处理和UI)直接用于移动设备上。但是,如果这样做就会降低J2ME的效果,导致缓慢的、浮肿的应用程序(它们只能部署在更昂贵的设备上)。在大多数情形中,我们应该选择为移动设备特别设计的轻量级的类库。通常多个有厂商在相同的市场上竞争,每个厂商提供一个稍微不同的轻量级产品,侧重于不同的特性。
CLDC和MIDP标准类库都是从下向上设计的轻量级组件。但是,遇到需要选择第三方类库的时候,选择正确工具的需求也适用于MIDP项目。对于特定的程序库,厂商为大量的MIDP设备(例如Symbian OS设备)提供一个与J2SE兼容的API版本和另一个使用专用API的非常轻巧的版本。一般来说,后者占用的内存更少、性能更好,但是需要额外的训练并且程序的移植性也较差。MIDP轻量级类库的例子有PointBase MIDP关系数据库API和iBus//Mobile JMS客户端API。
2、减少应用程序占用的空间
移动设备普遍只有很少的内存和存储空间,因此它要求我们同时优化应用程序的存储和运行时所占用的空间。下面是我们的一些建议:
· 优化程序打包(packaging)过程:尽管仔细地选择了最好的轻量级类库,但是我们仍然可以发现应用程序只使用了部分类库。在程序打包过程中,我们应该只包含实际使用的类。对于小型类库我们可以手动完成这种工作,对于大型类库可以使用与一些J2ME IDE(例如IBM WebSphere Studio Device Developer)绑定在一起的自动化工具来完成。如果你希望进一步减少二进制应用程序的大小,你可以使用字节码错乱程序(obfuscator)把长变量名称和类名称更换为更短的、含意模糊的名称。
· 分割应用程序:由于MIDP运行时只载入需要的类,我们可以把应用程序分成独立的部分以减少运行时占用的空间。对于MIDP应用程序,MIDlet工具套件可以包含多个相对独立的MIDlet。
尽管标准的MIDP规范并不支持共享类库,但是一些特定厂商的实现达到了这个目标。其中一个例子是BlackBerry手持设备的BlackBerry Java开发环境(JDE)。共享类库进一步减小了总体占用的空间,因为不需要在每个应用程序中重复类库。
3、最小化无用单元收集过程
Java的一个重要的优点是内建的无用单元收集器,它能够自动地释放无效对象使用的内存空间。这使得开发者可以把精力集中在核心逻辑上,而不用太关注内存管理的细节信息。其结果是,Java开发者一般不关心对象的创建。实际上,很多流行的Java设计模式进一步提升了建立更多的对象来代替更多的可维护代码的思想。
但是,在移动设备上,由于可以使用的内存总量很少,无用单元收集器必须频繁运行。当无用单元收集器运行的时候,它的线程占用宝贵的CPU周期并减慢其它所有应用程序的进程。如果要建立高效的J2ME应用程序,我们必须使对象的建立减少到最小,并迅速释放不再使用的对象。我们的建议如下:
· 在开发周期的早期阶段仔细地检查设计模式。例如,基于屏幕流程的方法就比传统的MVC实现使用的对象少很多。
· 在实现层简明地重复使用已有的对象。例如,如果一个相同的按钮(例如“DONE”按钮)出现在很多屏幕上,我们就应该一次建立,多次使用。
· 使用数组和StringBuffer(字符串缓冲)。数组比对象集合速度更快、内存效率更高。当我们修改或连接字符串的时候,不可变的String对象导致使用大量的中间对象,而StringBuffer的效率高很多。
· 在使用过后迅速关闭网络连接、文件句柄和记录管理系统(RMS)记录存储。我们应该仔细地在文档中找出close()、destroy()和dispose()方法并且明智地使用它们。目前有一条大家一致认同的经验:把这些方法都放在finally代码块中,以确保即使产生了运行时错误,这些资源也会被释放出来:
try { HttpConnection c =(HttpConnection) Connector.open("http://someurl"); InputStream is = c.openInputStream (); // 处理数据 } catch (Exception e) { // 处理异常 } finally { try { if ( c != null ) c.close(); if ( is != null ) is.close(); } catch (IOException ioe) { } } |
· 使用本地类库的时候释放资源。在智能移动应用程序中,有时候为了得到更高的性能我们会访问本地类库、受限制的功能(例如电话呼叫)或本地UI的外表和感觉(例如用于PocketPC的IBM SWT程序库)。本地资源不属于无用单元收集的范畴。在使用完一些资源以后,我们需要遵循适当的本地类库(和它们的Java包装类)的指令来释放它们。
4、使用移动入口(Portal)
智能移动设备越来越强大了。但是,在复杂的企业环境中,很多事务对于大多数移动设备来说还是对资源很敏感的。在这种情况下,普遍的方法是建立一个入口服务器,移动设备可以委托它执行一些复杂的事务。移动中间件入口作为移动客户端和企业后端服务器的桥梁。智能入口不仅仅是移动设备的代理或者代替。移动入口的使用包括:
· 允许移动客户端利用多种通讯和消息传递协议。例如,移动消息处理服务器应该把很大范围内的大量设备集成到共同的消息处理下部构造中。
· 聚合后端服务并激活捆绑(bundled)服务。例如,Oracle9iAS无线服务器为Oracle的SQL数据库提供了J2ME SDK、基于推送(Push)的消息传递和基于位置的服务。BlackBerry企业服务器支持BlackBerry MIDP设备对基于微软Exchange或IBM Lotus Domino的协同信息系统的统一标准的访问。
· 为强大的和复杂的后端服务提供简单的移动界面。
5、明智地使用设计模式
没有适合于任何环境的设计模式。例如,强大的MVC和Fa?ade模式需要多个抽象层,对于简单的应用程序来说可能太笨重了。对于简单的应用程序,我们可以围绕屏幕设计整个逻辑。