如果说J2ME和J2SE有什么最大的区别,那就是他们运行环境的不同.J2ME最主要的限制就在于它可用来存储数据和运行程序的内存空间太小.当前大多数支持MIDP的设备,都限制了应用程序不得超于50K大小,这点儿就是千兆级的J2ME的服务运行环境大相径庭.下面我们将学习一些技巧让J2ME程序最小化 .下面就是一个最小化程序大小的例子:
package com.j2medeveloper.techtips;
import javax.microedition.lcdui.*;
public class BeforeSizeOptimization extends
BasicMIDlet {
public static final Command exitCommand =
new Command( "Exit",
Command.EXIT, 1 );
public BeforeSizeOptimization(){
}
protected void initMIDlet(){
getDisplay().setCurrent( new MainForm() );
}
public class MainForm extends Form {
public MainForm(){
super( "MainForm" );
addCommand( exitCommand );
append( textf );
setCommandListener( new CommandListener(){
public void commandAction( Command c,
Displayable d ){
if( c == exitCommand ){
exitMIDlet();
}
}
}
);
setItemStateListener(
new ItemStateListener() {
public void itemStateChanged(
Item item ){
if( item == textf ){
AlertType.INFO.playSound(
getDisplay() );
}
}
}
);
}
private TextField textf =
new TextField( "Type anything", null,
20, 0 );
}
}
虽然这个MIDlet在此仅作为一个例子,但使用的尺寸优化技巧可以适用于任一J2ME的profile上。
注意,上面的MIDlet类需要下面的辅助类:
package com.j2medeveloper.techtips;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public abstract class BasicMIDlet extends MIDlet {
private Display display;
public BasicMIDlet(){
}
protected void destroyApp( boolean unconditional )
throws MIDletStateChangeException {
exitMIDlet();
}
public void exitMIDlet(){
notifyDestroyed();
}
public Display getDisplay(){ return display; }
protected abstract void initMIDlet();
protected void pauseApp(){
}
protected void startApp()
throws MIDletStateChangeException {
if( display == null ){
display = Display.getDisplay( this );
initMIDlet();
}
}
}
如果用J2ME Wireless Toolkit运行这个例子,程序的大小大约4K左右.
为了更好的说明该怎么做,我们列出缩小程序大小需要注意的事项:
1, 去掉不必要的类,保证程序结构的简洁 . 你有没有考虑过,真的所有的特性都是你的程序所需要的么?你的用户是否可以得到更简洁的东西?考虑到这点,你的程序已经为最小化做好了准备.
2, 第二步就是深入考察程序定义的内部类,特别是匿名类。记住,每个类文件都有一定量的与之相关的系统开销。即便最普通的类也有系统开销。
public class foo {
// nothing here
}
编译这个Class所在类文件,你需要200 bytes大小,再加上这个类的一些常规方法的实现,比如实现Event Listener接口等.而本身一个MIDlet就需要实现比如CommandListener, ItemStateListener接口, 如果可以的话,完全可以把他们用在一个文件中,多个类享受这一个commandAction , itemStateChanged ,不是很好么?虽然这需要你对程序的结构组织非常清晰:)
内部类也某些方面也会消耗内存空间 ,因为编译器需要产生一些特殊的变量和方法来提供内部类入口的私有信息.这点可以参考Sun的这片文章:
http://java.sun.com/products/archive/jdk/1.1/index.html
3 尽量使用现有的类。例如,基于CLDC的profile没有构造集合类,所以我们可以用内建的Hashtable和Vector类来实现之。构造MIDP程序时也可采用此法。例子MIDlet中定义了一个form字类来生成主表,可以容易的如下直接生成:
mainform = new form( "Mainform" );
mainform.addCommand( okCommand );
mainform.setCommandListener( listener );
这里没有对与错, 因为这是很容易理解的。
4, 破坏程序的继承关系。你也许把相关的代码放到一个或多个抽象类中,这是OOD中为提高程序间代码重用的推荐做法。虽然破坏继承关系与你所学知识相违背,但简化的继承关系更有意义。特别的,当你的的抽象类――可能来自其他项目――仅仅被继承一次时,破坏继承关系的结果不言而喻。例如,例子MIDlet继承了BasicMIDlet类,但两者合并为一个类。
5, 尽量减少你创建的包,类,方法和成员变量的命名长度 . 这个听起来似乎很无聊和愚蠢.不过一个类文件保存了许多的符号信息 简短的命名将会使您简化类文件 ,这个也许听起来不是那么紧要,不过当几个类衍生开去的时候,你就明白它的功效了.包已经有了一套成熟的简化命名方式,尽量避免包的完整命名 ----- 在没有和其他类冲突的情况下.
另外,简短命名的工作并不总是需要手动完成,您完全可以使用工具来帮您完成,
”混淆器” 就是一个不错的选择 .它的主要目的就是对命名的一个优化(对你原有命名方式的一个隐藏和缩减).而这个过程最大的效果就是收缩应用程序的大小.这主要归功于它对方法和数据变量的可读性的重新命名(在编译代码中).这有一个开发源码的软件,您可以在这里找到它: http://www.retrologic.com/ (RetroGuard) ,
记得在使用混淆器之前需要预审核(preverification) , 否则混淆器会使类文件中的预审核数据无效.
6, 数组的初始化方式 .一个数组的初始化声明如下:
int arr[] = { 0, 1, 2, 3 };
而实际编译代码如下::
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
这个过程可以通过使用Java 2 SDK中附带的javap工具把二进制代码反编译成类文件去看(使用-c选项)。也许你会诧异于看到的内容,特别当你希望看到的是一排排二进制常数时。有两种方法可以让你看不到反编译的程序代码,(1)把数据编码为字符串,运行时解码之,或者(2)把数据存为二进制文件并与程序打包,用类装载器的getResourceAsStream方法在运行时存取之。
以上只是一个指导方针,对J2ME程序而言,这里并没有提到所有的步骤,不过大多数方法都可以应用到当前例子.最优化的MIDP例子如下:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class ASO extends MIDlet
implements CommandListener,
ItemStateListener {
private Display display;
private Form mainForm;
private TextField mainFormTF =
new TextField( "Type anything", null,
20, 0 );
public static final Command exitCommand =
new Command( "Exit",
Command.EXIT, 1 );
public ASO(){
}
public void commandAction( Command c,
Displayable d ){
if( c == exitCommand ){
exitMIDlet();
}
}
protected void destroyApp( boolean unconditional )
throws MIDletStateChangeException {
exitMIDlet();
}
public void exitMIDlet(){
notifyDestroyed();
}
public Display getDisplay(){ return display; }
protected void initMIDlet(){
mainForm = new Form( "MainForm" );
mainForm.addCommand( exitCommand );
mainForm.setCommandListener( this );
mainForm.setItemStateListener( this );
mainForm.append( mainFormTF );
getDisplay().setCurrent( mainForm );
}
public void itemStateChanged( Item item ){
if( item == mainFormTF ){
AlertType.INFO.playSound( getDisplay() );
}
}
protected void pauseApp(){
}
protected void startApp()
throws MIDletStateChangeException {
if( display == null ){
display = Display.getDisplay( this );
initMIDlet();
}
}
}
关于作者:Eric Giguere是来自Sybase下属iAnywhere Solutions的软件开发人员。他致力于手持设备和无线计算领域的Java技术。他是滑铁卢大学的数学学士和数学硕士,写了很多有关计算的文章。