要把J2ME程序與J2SE程序區(qū)分開(kāi)來(lái),其依據(jù)就是J2ME運(yùn)行所處的受限環(huán)境。多數(shù)J2ME系統(tǒng)的主要受限條件就是可以存儲(chǔ)和運(yùn)行程序所需內(nèi)存的大小。例如,許多MIDP設(shè)備限制應(yīng)用程序的尺寸不大于50K,這遠(yuǎn)遠(yuǎn)不及Server端J2SE運(yùn)行環(huán)境下那些成兆的程序。實(shí)際應(yīng)用中,程序會(huì)很容易超出這些限制條件。通過(guò)本篇您將學(xué)到一些減小程序尺寸大小的技巧,并在下面的例子中實(shí)踐這些技術(shù)。這個(gè)例子MIDlet僅僅顯示一個(gè)文本框并在其內(nèi)容改變時(shí)發(fā)聲。
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 );
}
} |
雖然這個(gè)MIDlet在此僅作為一個(gè)例子,但使用的尺寸優(yōu)化技巧可以適用于任一J2ME的profile上。
注意,上面的MIDlet類(lèi)需要下面的輔助類(lèi):
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 WTK打包時(shí),本例子MIDlet占用4K空間。
減小尺寸的首要步驟就是通過(guò)修正程序的功能實(shí)現(xiàn)來(lái)去掉多余的類(lèi)。程序的所有功能確實(shí)必須都實(shí)現(xiàn)嗎?用戶(hù)可以不需要這些“附屬功能”嗎?要設(shè)計(jì)盡可能小的程序,這里的MIDlet例子已經(jīng)相當(dāng)小了。
第二步就是深入考察程序定義的內(nèi)部類(lèi),特別是匿名類(lèi)。記住,每個(gè)類(lèi)文件都有一定量的與之相關(guān)的系統(tǒng)開(kāi)銷(xiāo)。即便最普通的類(lèi)也有系統(tǒng)開(kāi)銷(xiāo)。
public class foo {
// nothing here
} |
編譯上邊的類(lèi),生成的類(lèi)文件大約200byte大小。比如實(shí)現(xiàn)一個(gè)事件監(jiān)聽(tīng)器,就是對(duì)匿名類(lèi)的常見(jiàn)使用。在例子MIDlet中就定義了兩個(gè)此類(lèi)的監(jiān)聽(tīng)器。接下來(lái)進(jìn)行的最簡(jiǎn)單的優(yōu)化就是,讓主MIDlet類(lèi)實(shí)現(xiàn)CommandListener和ItemStateListener接口,并把監(jiān)聽(tīng)器代碼移至此處。記住,多個(gè)對(duì)象可以使用同樣的監(jiān)聽(tīng)器。必要時(shí),可以使用傳遞至commandAction和itemStateChanged方法的參變量來(lái)區(qū)分它們。
內(nèi)部類(lèi)也可使代碼過(guò)大,因?yàn)榫幾g器必須生成特殊的變量和方法,以便內(nèi)部類(lèi)可以訪問(wèn)包含它們的類(lèi)的私有內(nèi)容。請(qǐng)參考內(nèi)部類(lèi)的規(guī)范以獲取更多信息。
第三步,盡量使用現(xiàn)有的類(lèi)。例如,基于CLDC的profile沒(méi)有構(gòu)造集合類(lèi),所以我們可以用內(nèi)建的Hashtable和Vector類(lèi)來(lái)實(shí)現(xiàn)之。構(gòu)造MIDP程序時(shí)也可采用此法。例子MIDlet中定義了一個(gè)form字類(lèi)來(lái)生成主表,可以容易的如下直接生成:
mainform = new form( "Mainform" );
mainform.addCommand( okCommand );
mainform.setCommandListener( listener ); |
這里沒(méi)有正確或者錯(cuò)誤的答案,只是要推敲。
第四步就是破壞程序的繼承關(guān)系。你也許把相關(guān)的代碼放到一個(gè)或多個(gè)抽象類(lèi)中,這是OOD中為提高程序間代碼重用的推薦做法。雖然破壞繼承關(guān)系與你所學(xué)知識(shí)相違背,但簡(jiǎn)化的繼承關(guān)系更有意義。特別的,當(dāng)你的的抽象類(lèi)――可能來(lái)自其他項(xiàng)目――僅僅被繼承一次時(shí),破壞繼承關(guān)系的結(jié)果不言而喻。例如,例子MIDlet繼承了BasicMIDlet類(lèi),但兩者合并為一個(gè)類(lèi)。
第五步就是要縮短名字長(zhǎng)度,如包名、類(lèi)名、方法名和數(shù)據(jù)元素名。看起來(lái)有些蠢,但一個(gè)類(lèi)文件確實(shí)包含太多的符號(hào)信息??s短各量的名字可以縮小生成的類(lèi)文件尺寸。這種節(jié)省不會(huì)特別明顯,但多個(gè)類(lèi)中進(jìn)行總加的結(jié)果還是可觀的。包名對(duì)減小尺寸來(lái)講特別合適。MIDP程序是完全自我包容的,完全可以不使用包名,因?yàn)樵谑殖衷O(shè)備上包名根本不可能與其他類(lèi)名沖突。例子MIDlet中,可以把com.j2medeveloper.tchtips包名去掉。
注意,一般來(lái)講,縮短名字不需要手工去做,要用一個(gè)“混淆器”去做。“混淆器”的主要功能是“隱藏”程序代碼,使之不能通過(guò)反編譯讀出。它的副作用是減小了程序的尺寸。因?yàn)殡[藏過(guò)程主要通過(guò)更改方法和數(shù)據(jù)成員的名字來(lái)完成。有一個(gè)開(kāi)源的混淆器稱(chēng)為RetroGuard,可以免費(fèi)從http://www.retrologic.com得到。也有一些商業(yè)包可用。(當(dāng)為基于CLDC的profile混淆時(shí),記得在預(yù)校驗(yàn)之前混淆,否則混淆器將使類(lèi)文件中的預(yù)校驗(yàn)數(shù)據(jù)失效。)
最后,深入數(shù)組的初始化。(例子MIDlet沒(méi)有做數(shù)組初始化,但對(duì)程序來(lái)說(shuō)初始化是重要的一步) 在編譯時(shí),一個(gè)數(shù)組初始化聲明如下所示:
int arr[] = { 0, 1, 2, 3 };
而實(shí)際生成代碼的過(guò)程如下所示:
<ccid_nobr>
<table width="400" border="1" cellspacing="0" cellpadding="2"
bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center">
<tr>
<td bgcolor="e6e6e6" class="code" style="font-size:9pt">
<pre><ccid_code> arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3; |
這個(gè)過(guò)程可以通過(guò)使用Java 2 SDK中附帶的javap工具把二進(jìn)制代碼反編譯成類(lèi)文件去看(使用-c選項(xiàng))。也許你會(huì)詫異于看到的內(nèi)容,特別當(dāng)你希望看到的是一排排二進(jìn)制常數(shù)時(shí)。有兩種方法可以讓你看不到反編譯的程序代碼,(1)把數(shù)據(jù)編碼為字符串,運(yùn)行時(shí)解碼之,或者(2)把數(shù)據(jù)存為二進(jìn)制文件并與程序打包,用類(lèi)裝載器的getResourceAsStream方法在運(yùn)行時(shí)存取之。
以上只是一些指導(dǎo)性的方法,對(duì)每個(gè)J2ME程序而言,這里沒(méi)有具體到步驟。但是多數(shù)方法可以應(yīng)用的本例。優(yōu)化后的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();
}
}
} |
關(guān)于作者:Eric Giguere是來(lái)自Sybase下屬iAnywhere Solutions的軟件開(kāi)發(fā)人員。他致力于手持設(shè)備和無(wú)線(xiàn)計(jì)算領(lǐng)域的Java技術(shù)。他是滑鐵盧大學(xué)的數(shù)學(xué)學(xué)士和數(shù)學(xué)碩士,寫(xiě)了很多有關(guān)計(jì)算的文章。