最佳化J2ME程式大小 (轉)

amyz發表於2007-08-15
最佳化J2ME程式大小 (轉)[@more@]

如果說和有什麼最大的區別,那就是他們執行環境的不同.J2ME最主要的限制就在於它可用來資料和執行的空間太小.當前大多數支援MIDP的裝置,都限制了應用程式不得超於50K大小,這點兒就是千兆級的J2ME的服務執行環境大相徑庭.下面我們將學習一些技巧讓J2ME程式最小化 .下面就是一個最小化程式大小的例子:

 :namespace prefix = o ns = "urn:schemas--com::office" />

package com.j2medeveloper.tech;


 


import x.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 MainFoextends 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的這片文章: 

/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,  儘量減少你建立的包,類,方法和成員變數的命名長度 . 這個聽起來似乎很無聊和愚蠢.不過一個類檔案儲存了許多的符號資訊 簡短的命名將會使您簡化類檔案 ,這個也許聽起來不是那麼緊要,不過當幾個類衍生開去的時候,你就明白它的功效了.包已經有了一套成熟的簡化命名方式,儘量避免包的完整命名 ----- 在沒有和其他類衝突的情況下.

  另外,簡短命名的工作並不總是需要手動完成,您完全可以使用工具來幫您完成,

”混淆器” 就是一個不錯的選擇 .它的主要目的就是對命名的一個最佳化(對你原有命名方式的一個隱藏和縮減).而這個過程最大的效果就是收縮應用程式的大小.這主要歸功於它對方法和資料變數的可讀性的重新命名(在編譯程式碼中).這有一個開發原始碼的,您可以在這裡找到它:   (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)把資料存為二進位制檔案並與程式打包,用類裝載器的getReAsStream方法在執行時存取之。

 

 

  以上只是一個指導方針,對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是來自下屬iAnywhere Solutions的人員。他致力於手持裝置和計算領域的Java技術。他是滑鐵盧大學的數學學士和數學碩士,寫了很多有關計算的文章。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-957526/,如需轉載,請註明出處,否則將追究法律責任。

相關文章