Java ME设备的多样化无疑是移动应用开发的噩梦,屏幕大小、按键键值、性能高低和API支持的不同等众多的差异为Java ME应用的多终端移植开发带来了重重困难。
预处理器(Preprocessor),顾名思义,是在编译之前对源代码进行预先处理。它为JavaME应用的多终端移植提供了一个良好的解决方案,你可以让多个终端版本共用同一套源代码,基于不同终端的属性对代码预处理后输出不同版本,这为移植和代码维护都带来了巨大的便利。
目前比较常见的预处理器有以下几个方案:
- J2me Polish:http://www.j2mepolish.org
- Antenna:http://antenna.sourceforge.net
- Netbeans的Mobility Pack: http://www.netbeans.org/products/mobility/
这三个方案的使用方式基本相同,本文选择Netbeans为例来说明预处理器在多终端移植开发中的使用,相比其他两个方案Mobility Pack的解决方案跟IDE结合的更紧密,易用性更高。
本文仅介绍和预处理器使用的相关内容,关于Netbeans的安装、使用和技巧,请参考Netbeans的帮助文档及其官方网站提供的相关文档。
本文内容基于Netbeans 5.5和Mobility Pack 5.5。
2. 使用配置(Configuration)
首先,在使用Netbeans的预处理器之前,需要先了解一下配置(Configuration)。这里的配置区别于JavaME架构中的配置,是Mobility Pack引入的一个概念,通过配置,我们可以为每一个目标设备定义一个执行环境。
在一个MIDP工程中,我们可以添加任意多个配置,每个配置中可以定义的内容包括:
- 平台(Platform),包含仿真器平台、API包等;
- 仿真器设备,指定特定的仿真器;
- 性能(Ability),目标设备的能力列表,包含API支持情况、屏幕大小、文件类型支持、自定义参数等设备相关属性;
- 应用程序描述,JAD和JAR清单文件中的描述内容;
- Build参数,包含源文件过滤、编译参数、类库、资源文件、混淆参数、JAD/JAR文件、签名和Javadoc生成等Build相关参数;
- 部署参数,包含仿真器命令行和部署方式等;
配置的新建、编辑和删除可以在工程的属性页完成,更多详细的内容可以参考Netbeans的帮助文档。
配置中定义的性能列表是预处理器工作的一个重要依据。
3.使用预处理器
预处理器在编译器工作之前发挥作用,根据配置中的定义,提前修改源文件中的代码。预处理器修改代码的依据是预处理指令,这些指令以特定的注释方式插入在源文件中,每一个指令都紧跟在”//#”之后。预处理器修改代码的方式是注释,如果预处理器指令成立,则指令包含的部分被保留,反之,则会被注释掉。
举例说明,在游戏开发中使用全屏显示,在支持NOKIAUI的设备上,我们可以使用FullCanvas,在MIDP2.0设备上,我们可以使用Canvas或者GameCanvas中的setFullMode(boolean)方法。
假设我们已经定义了两个配置:NOKIA_7210和NOKIA_N70,两个配置的性能列表片段如下:
性能 NOKIA_7210 NOKIA_N70 NOKIAUI 1.1 1.1 MIDP 1.0 2.0 ……
代码片段如下:
//#if MIDP == “2.0” import com.nokia.ui.FullCanvas; //#elif NOKIAUI //# import javax.microedition.lcdui.GameCanvas; //#endif
注:上面的//#import javax.microedition.lcdui.GameCanvas;部分的注释为Netbeans自动完成的内容,在编写代码过程中不用添加,IDE会根据当前的选择的配置自动注释需要删除的部分。
当配置NOKIA_N70被选中后,import javax.microedition.lcdui.GameCanvas;会被注保留,反之如果是NOKIA_7210被选中,则import com.nokia.ui.FullCanvas;会被保留。了解了预处理器的工作原理之后,我们还需要进一步了解的内容就是预处理指令集的语法、功能和用法。关于这一点,在参考资料1和Netbeans的帮助文档中都有非常详细的介绍,请大家参考相关内容,本文不在赘述。
4.预处理器之外
通过预处理器我们可以让一份源代码文件适用于多个设备,但是往往在多终端移植开发中,源代码并不是唯一需要关注的内容。由于屏幕大小、MIDlet图标规格、可用堆栈大小和音乐文件格式等诸多差别的存在,我们还需要在不同设备的版本中使用特定的资源文件,对于游戏应用来说这一点尤为重要。
针对这个问题,我们可以灵活使用配置定义中的“类库与资源文件”。通过“工程属性->Build->类库与资源”可以进入类库与资源的定义页面,每个配置都可以设置不同的类库与资源。
我们可以把不同版本的资源放置在各自独立的文件夹中,通过添加资源文件夹为不同配置定义资源文件。这样不同的配置就可以使用其特定的资源文件了。
此外,由于设备的API支持差别,不同的设备可能需要添加不同的类库,也可以通过同样的方式解决。例如,在CLDC1.0的设备上,需要添加定点数API来完成对浮点数的模拟。
注:在类库与资源的定义中,只可以添加整个文件夹,所以文件夹中可能包含类似.svn和Thumb.db等不需要的文件。我们需要将这些文件排除在资源文件列表之外,在IDE中并没有直接提供文件类型过滤的方法,好在Netbeans是基于Ant来完成Build工作的,我们可以通过修改Build.xml来完成文件类型过滤。
我们可以打开${工程目录}/nbproject/build-impl.xml文件,将
<target name="pre-init">
修改为:
<target name="pre-init"> <defaultexcludes add="**/*.db"/> <defaultexcludes add="**/*.其他文件类型"/> </target>
Ant已经默认排除了部分文件类型,例如.svn文件类型,详细内容请参考Ant的相关文档。
5.存在的问题
预处理器的引入可以为多终端移植工作带来明显的好处,终于可以达成一份代码适用于多个平台的宿愿了,可以大大降低多版本维护的成本,提高开发效率等。
然而,有一利必有一弊,这一份代码却是要经过一次又一次修改的,显然,代码的每一次修改都会带来新的风险,已经通过测试的版本可能又会