作者:Eric Giguere 编译:Sean
2002年2月27号
如果要说J2ME应用程序和J2SE应用程序有什么不同的地方的话,那就是他们各自被限制运行的环境。
很多J2ME系统的主要的瓶颈是存储和运行应用程序的可用内存数量。举例来说,当前许多MIDP设备,他们
限制给应用程序的内存数量就只有50K或更少,离可能要求兆级的基于服务端J2SE环境有段很长的距离。
由于你在开发中会很容易就遭遇这些限制,所以在这篇J2ME技术提示中,你会学到如何让你的应用程序占用最
少的内存。你将用这些技术减少MIDlet占用的空间,MIDlet只是显示一个文本框并且在其中内容被更改
的时候发出声音:
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程序。
注意上面演示的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打包以后,这个MIDlet例子仅仅4K多一点。
减少size的第一步是通过减少应用程序功能来移除一些不必要的类。你的应用程序的每个特性都是真的必要的吗?
你的用户能否忍受没有铃声和哨声?让我们来建立最小版本的应用程序。注意到MIDlet例子已经是相当小的了。
第二步着眼于应用程序定义的内部类,特别是匿名类。记住每一个类文件都有一定相应的空间开支。甚至最微不足道的
类也需要成本:
public class foo {
// nothing here
}
编译这个类,你可以发现你得到一个占用几乎200字节空间的class文件。一个匿名类一般用途是用来实现事件监听。
MIDlet例子定义了两个listener。一个简单优化就是让MIDlet的主类实现CommandListener和ItemStateListener
两个接口,删除里面的listener代码。记住多个对象可以使用相同的listener。如果需要区别开来,
可以传递参数给commanAction和itemStateChanged方法。
内部类也会通过其他方面膨胀代码,因为编译器必须产生不同的变量和方法来允许一个内部类访问封装类里面的私有信息。
第三步是最大程度使用内置的类。举个例子,在基于CLDC的程序中不要建立你自己的集合类。尽量使用内置的Hashtable
和Vector。设计MIDP应用也是一样。MIDlet例子定义了一个Form子类来创建它的主form,但它还能被很容易地直接创建:
mainForm = new Form( "MainForm" );
mainForm.addCommand( okCommand );
mainForm.setCommandListener( listener );
这里没有对和错的问题,这只是需要考虑的一些简单的东西。
第四步是取消你应用程序的继承关系。你可能已经将代码分解成一个或多个抽象类,这是个被推荐的能提高代码重用
的OOD设计方法。也许会跟你学到的东西有抵触,但去简化继承层次更加说的过去。如果你的抽象类——可能来自其他项目——
只是被继承了一次,那简化继承关系就显得特别有理由。MIDlet例子扩展了BaicMIDlet类,但两个类很容易组合成一个。
第五步是缩短你的包名,类名,方法名和数据成员名。看上去这一步显得很愚蠢,但一个class文件保存了很多字符信息。
缩短他们的名字你会减少clas文件的size。这个办法节省的东西不会很多,但当遍及到多个类时他们会增加。包名特别
应该被缩短。因为MIDP应用程序完全是独立的,你能完全避免包名——不会有机会跟该设备其他类的名字冲突。MIDlet例子
可以从com.j2medeveloper.techtips 包里面移出来。
注意缩短名字不是你手工要做的那些东西,我们可以使用一个名叫“扰乱器”的工具来做。扰乱器的主要目的是
为应用程序“隐藏”代码,它反编译程序的时候使程序变成一些几乎无法阅读的东西。这个过程的另外一个作用就是减少了程序的size。
那上因为隐藏代码主要通过重新为方法和数据成员命名。这里有个开发源代码的扰乱器叫RetroGuard的免费工具来自
http://www.retrologic.com,并且上面还有许多商业用的包。
最后,关注一些数组的初始化。(MIDlet例子没有做任何数组初始化,但这是程序很重要的一步)当编译完以后,一个数组初始化
代码像这样:
int arr[] = { 0, 1, 2, 3 };
实际产生的代码如下:
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
如果想研究的话,可以使用java2sdk一起发布的将字节代码分解成一个class的javap工具(使用-c参数)。你可能会惊讶和不满
你所看到的结果。两种可选的办法是(1)把数据编码到一个String里面,运行时解码到数组,或(2)将数据作为二进制文件保存
到程序包里面并在运行时用class loader的getReourceAsStream方法访问。
这里说的东西只是一些指导,并且在这里提到的不是每一步都适合每一个J2ME应用程序。然而,他们大部分都可以应用这个MIDlet例子。
MIDlet的优化版本如下:
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();
}
}
}
原文:http://wireless.java.sun.com/midp/ttips/appsize/