如果要說J2ME應用程序和J2SE應用程序有什麼不同的地方的話,那就是他們各自被限制運行的環境。
Java手機網[www.cnjm.Net]很多J2ME系統的主要的瓶頸是存儲和運行應用程序的可用內存數量。舉例來說,當前許多MIDP設備,他們
限制給應用程序的內存數量就只有50K或更少,離可能要求兆級的基於服務端J2SE環境有段很長的距離。
Java手機網[www.cnjm.Net]由於你在開發中會很容易就遭遇這些限制,所以在這篇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 );
Java手機網[www.cnjm.Net] public BeforeSizeOptimization(){;
};
protected void initMIDlet(){;
getDisplay().setCurrent( new MainForm() );
};
public class MainForm extends Form {;
public MainForm(){;
Java手機網[www.cnjm.Net] super( "MainForm" );
addCommand( exitCommand );
append( textf );
setCommandListener( new CommandListener(){;
public void commandAction( Command c,
Displayable d ){;
if( c == exitCommand ){;
exitMIDlet();
};
};
};
);
Java手機網[www.cnjm.Net] setItemStateListener(
new ItemStateListener() {;
Java手機網[www.cnjm.Net] public void itemStateChanged(
Item item ){;
if( item == textf ){;
AlertType.INFO.playSound(
getDisplay() );
};
};
Java手機網[www.cnjm.Net] };
);
};
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 );
Java手機網[www.cnjm.Net] 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(){;
Java手機網[www.cnjm.Net] };
Java手機網[www.cnjm.Net] public void commandAction( Command c,
Displayable d ){;
if( c == exitCommand ){;
exitMIDlet();
};
};
protected void destroyApp( boolean unconditional )
Java手機網[www.cnjm.Net] throws MIDletStateChangeException {;
exitMIDlet();
};
Java手機網[www.cnjm.Net] public void exitMIDlet(){;
Java手機網[www.cnjm.Net] notifyDestroyed();
Java手機網[www.cnjm.Net] };
public Display getDisplay(){; return display; };
protected void initMIDlet(){;
mainForm = new Form( "MainForm" );
mainForm.addCommand( exitCommand );
mainForm.setCommandListener( this );
Java手機網[www.cnjm.Net] 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();
};
};
};