J2ME(Java 2 Micro Edition)的出现把Java开发者彻底地引入了开发无线设备应用程序的广阔天地。当然,移动设备因其功能和处理能力而多种多样,但是,J2ME通过定义configurations和profiles而对这些差异性进行了逻辑抽象,然后整合两种设置为在特定移动设备上开发应用程序提供了一套完整的平台和API。
连接限制设备配置CLDC(Connected Limited Device Configuration)和移动信息设备设置MIDP(Mobile Information Device Profile)一道支持目前在用的大多数主流移动设备,比如PDA、手机和双向寻呼机等。本文将通过J2ME构建一个简单的示范应用程序向读者介绍其基本知识。
J2ME示例程序
实际构建、安装和运行MIDP应用程序的过程同标准Java应用程序存在很大不同。为了“公平”起见,这一问题只能留待今后的文章对其进行专门阐述了。现在我们就来了解了解示例程序的结构。这个程序就是程序员刚入门时几乎都要遭遇的“Hello World!”应用程序,在这里,程序名是HelloJ2ME,程序代码见清单A。运行HelloJ2ME程序后的显示结果见图A。
我知道,这个程序非常简单,不过要作为一种讲解新语言的入门示例也足够了。正如你看到的那样,HelloJ2ME扩展了MIDP基本应用程序MIDlet类并导入了两个包名称空间: javax.microedition.midlet和javax.microedition.lcdui。前者包括该应用程序的MIDlet基类,而后者则提供了一组类似Swing的GUI元素供应用程序使用。
MIDlet类提供了三种抽象方法供设备的应用程序管理器用来同其运行的应用程序通讯。只要应用程序被激活、构造器执行完毕之后就会立即调用startApp方法而不是在应用程序最初启动的时候这样做。应用程序在一次运行的过程中会在活动和不活动状态之间多次转变,这样你就不必编写单独运行的初始化代码了,比如初始化用户界面的代码等,因为这类代码很可能会执行好多次。为此应该采用构造器来完成同一功能。
管理器指示应用程序关闭之后就会调用destroyApp方法。和startApp方法不一样的是该方法只在应用程序生存期内调用一次,所以在这个方法内编写清除代码是很安全的。实际上,由于MIDP并没有为对象包括finalize函数,所以你不得不在以上方法处执行清除功能。同时,由于典型的移动设备比通常情况下的标准平台欠缺稳定,经常被用户进行开关机或者复位操作。所以你也不能真正指望destroyApp派上大用场。
最后的抽象方法就是pauseApp了。该方法主要作用是发出这样的通知:因为用户转换到其他应用或者采用了设备的某项功能促使应用程序不能继续运行而暂时停止应用程序的运行。由于大多数移动设备都缺乏执行多任务的处理能力,以上的这类情况是完全可能发生的。所以在这个方法中应该编码释放所有资源。一旦应用程序重新开发运行则应用程序管理器会再度调用startApp方法。
同应用程序管理器的通讯
当然,通讯必须是双向有效的,MIDP应用程序也不例外。MIDlet提供了一组方法供你用来同应用程序管理器通讯:
NotifyDestroyed告诉管理器你的应用程序要关闭了。调用该方法不会执行destroyApp方法,所以你必须手工调用它。
NotifyPaused通知管理器你的应用程序要暂停了。
ResumeRequest要求应用程序管理器重启暂停的应用程序。
GetAppProperty从输入或者应用程序描述文件中获取应用程序的配置信息,这些将在以后讨论。现在我们不妨认为该方法就是访问专有的初始化文件。
管理界面
我刚才已经提到过, javax.microedition.lcdui包包含了MIDP应用程序要采用的用户界面元素。这个包里的大多数UI(用户界面)元素同Swing的对等物非常相似,只是名字不同而已,其API后台的事件系统在工作方式上也基本一样。此外,同HelloJ2ME 程序的构造器中代码一样,你应该把事件侦听器对象注册为控件。这样,为简单起见,HelloJ2ME即可实现自身的commandListener接口并为其包含的唯一Command对象起到侦听器的作用。不过,创建侦听器类、匿名内部类和专用类的其他技术也可以实现以上目标。
GUI组件族
lcdui包内基本上定义了三组GUI组件,我是根据其基类对它们分组的,它们是Screen组件、Item组件和Miscellaneous Displayable组件。
Screen组件
Screen组件派生于Screen抽象类,其作用是提供传统的、window风格的GUI控件。HelloJ2ME程序所采用的Form对象就是Screen类的派生,其中包含和显示GUI控件。其他Screen组件还包括Alert对话框和显示多组选项的List以及容纳多行条目的TextBox等。
Item组件
Item组件就是传统的控件,比如“Hello World!”程序的TextField等,它们都派生于Item抽象类,后者提供了标签、事件处理和显示控件的统一API。ChoiceGroup、 DateField、 Gauge、 ImageItem和StringItem则是其他类型的Item组件。
Miscellaneous Displayable 组件
Miscellaneous Displayable组件都派生于高级的Displayable抽象类或者具有同其相似的工作方式。该组组件有Command命令按钮、显示滚动文本的Ticker以及显示图象的Graphics等,此外还包括操作预定义项的Choice界面等。以上这些组件无法归于其他两类,所以单独组成一组归类。
图B所示就是这些组件的关系层次。
以上的全体图形控件都是由Display对象管理的,每一个应用程序都会访问这一对象的单一、私有实例。该实例可以通过静态的Display.getDisplay方法获得,该方法通常会把指向该实例的引用保存在一个成员变量里,HelloJ2ME在其构造器中就是这样做的。除了为特定屏幕元素设置焦点(setCurrent)和获取元素焦点的方法(getCurrent)之外,Display还暴露了一些用于获得设备显示能力信息的方法,比如是否显示彩色的(isColor)和支持显示色彩数量的(numColors)等方法。
小结
以上内容是为移动平台开发Java应用程序的一些基础知识。首先,你必须处理一个简单的类库:没有反射、JNI也没有我所提到的finalization支持。其次,你必须想法克服内存的局限性,因为大多数运行MIDP profile的移动设备可用的动态内存不会超过100K。这样,你就需要特别在意算法对内存的利用率。最后,你还要清楚地认识到,任何网络连接都会受到带宽和环境的限制。