• Thứ Bảy, 05/11/2005 10:54 (GMT+7)

    Tối ưu kích thước ứng dụng J2ME

    Ứng dụng J2ME bị nhiều giới hạn hơn so với ứng dụng J2SE, trước hết là giới hạn về dung lượng bộ nhớ để lưu trữ và chạy ứng dụng. Nhiều thiết bị MIDP giới hạn kích thước ứng dụng chỉ 50K hoặc ít hơn – nhỏ hơn nhiều so với các ứng dụng nhiều megabyte trong môi trường J2SE. Để đối phó với vấn đề này, bài viết giới thiệu một số kỹ thuật tối ưu kích thước ứng dụng J2ME (MIDlet).

    Ví dụ, chúng ta thực hiện tối ưu kích thước ứng dụng sau:

    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 );

    }

    }

    Lưu ý: lớp BasicMIDlet ở trên phụ thuộc vào lớp (class) sau:

    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();

    }

    }

    }

    Khi đóng gói ứng dụng bằng J2ME Wireless Toolkit thì MIDlet này có kích thước 4K byte (chính xác là 3,51kb).

    Bước 1: xây dựng phiên bản ứng dụng tối thiểu để giảm kích thước, loại bỏ những class không cần thiết.

    Bước 2: kiểm tra inner class bất kỳ được định nghĩa bởi ứng dụng, đặc biệt là các class vô danh (anonymous). Nhớ rằng mỗi tập tin class đều có kích thước, ngay cả class không có gì cũng chiếm ít nhất 200 byte, như:

    public class foo {

    // không có gì ở đây

    }

    Sau khi biên dịch, tập tin foo.class này có kích thước 180 byte.

    Cách dùng chung nhất đối với anonymous class là cài đặt event listener. MIDlet mẫu trên định nghĩa 2 listener theo cách này. Cách tối ưu dễ dàng là tạo ra class MIDlet chính, cài đặt luôn các interface CommandListener và ItemStateListener, rồi chuyển mã listener đến đó luôn.

    Nhớ rằng nhiều đối tượng có thể dùng chung một listener. Dùng các đối số cho các phương thức commandAction và itemStateChanged để phân biệt giữa chúng nếu cần thiết.

    Bước 3: tối đa việc dùng các class được cài đặt trước. Chẳng hạn, trong các profile dựa trên CLDC, đừng tự xây dựng tập các class Collection mà hãy dùng các class Hashtable và Vector. Tương tự với việc tạo form trong các ứng dụng MIDP, MIDlet mẫu định nghĩa một subclass Form để tạo form chính cho nó, nhưng nó chỉ có thể tạo trực tiếp như sau:

    mainForm = new Form( “MainForm” );

    mainForm.addCommand( okCommand );

    mainForm.setCommandListener( listener );

    Bước 4: giảm các phân lớp kế thừa bằng cách tạo mã chung thành một hoặc nhiều class abstract, một kỹ thuật được khuyến khích cho thiết kế hướng đối tượng đẩy mạnh việc dùng lại mã giữa các ứng dụng. Điều này có thể mâu thuẫn với những gì được học nhưng cần đơn giản phân lớp kế thừa. Điều này đặc biệt đúng nếu class abstract (có được từ dự án khác) là subclass. Chẳng hạn, MIDlet mẫu mở rộng BasicMIDlet nhưng hai class dễ dàng được kết hợp thành một class duy nhất.

    Bước 5: cần làm ngắn tên của các gói, class, phương thức và thành phần dữ liệu. Mọi tên được làm ngắn gọn và bạn sẽ giảm được kích thước của tập tin class. Việc tiết kiệm này không đáng kể nhưng có ý nghĩa khi tăng nhiều class.

    Lưu ý, việc làm ngắn tên có thể dùng một công cụ được gọi là “obfuscator”. Mục đích ban đầu của obfuscator là ẩn mã cho một ứng dụng bằng cách làm nó gần như không đọc được khi biên dịch ngược. Kết quả của việc xử lý này là làm giảm kích thước của ứng dụng. Có một obfuscator mã nguồn mở miễn phí là RetroGuard (xem bài “Tối ưu ứng dụng J2ME với RetroGuard”, ID: A0409_158), và cũng có các obfuscator thương mại.

    Bước 6: xem xét cẩn thận việc khởi tạo mảng. Khi được biên dịch, biểu thức khởi tạo mảng như sau:
    int arr[] = { 0, 1, 2, 3 };

    sinh ra mã:

    arr[0] = 0;

    arr[1] = 1;

    arr[2] = 2;

    arr[3] = 3;

    Hai phương thức thay thế là:

    (1) Mã hóa dữ liệu thành string và giải mã thành mảng lúc runtime, hoặc

    (2) Lưu dữ liệu như một tập tin nhị phân được đóng gói với ứng dụng và tạo khả năng có thể truy cập lúc runtime dùng phương thức getResourceAsStream của class loader.

    Đây chỉ là hướng dẫn, và không phải mỗi bước được đề cập ở đây đều cần cho các ứng dụng J2ME. Phiên bản tối ưu của MIDlet trên như sau:

    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();

    }

    }

    }

    Kết quả sau khi tối ưu: tập tin được đóng gói chỉ có 2kb (chính xác là 1,48kb).

    Dùng công cụ obfuscator RetroGuard, kết quả tập tin đóng gói vẫn 2kb (chính xác là 1,31kb).

    Lê Thanh Vũ
    JavaVietnam.org
    ---------------------------------------------
    Tài liệu tham khảo tại:
    http://java.sun.com
     

    ID: A0510_133