天天看點

Effective J2ME (2)

本文節選于筆者在數年前開發手機遊戲時總結的一份文檔。一家之言,贻笑大方。

2 J2ME開發技巧

2.1 性能

    在模拟器上可以使用JProbe Profiler等分析工具來分析性能瓶頸。但是需要注意的是模拟器的瓶頸與實機中的瓶頸可能存在差别。

2.1.1 設計可重用的對象

    在Java程式中,對象的建立是個不小的開銷。同時過多的無用對象也會導緻耗時的垃圾回收。是以在程式中,每次編碼類似 mBullet=null;的語句的時候,都要考慮一下這個對象是否可以被重用。推薦使用設計模式之一的工廠模式來控制對象的建立。例如在雷鳥号遊戲的子彈類(在整個戰鬥場景中,子彈需要不停地被建立和銷毀)的設計中,其構造函數被定義為私有,同時提供一個公共的靜态方法public static Bullet newInstance(…);來建立子彈的執行個體。這樣就可以對子彈執行個體的建立進行有效的控制。子彈類也定義了方法public void destroy();來控制子彈執行個體的銷毀。destroy();方法并不是簡單的将執行個體引用指派成null,而是将這個執行個體儲存到一個緩沖(程式中稱之為資源回收筒)裡,當程式需要建立新的子彈執行個體的時候,newInstance方法首先檢查資源回收筒是否儲存有使用過的子彈執行個體,如果沒有就調用子彈類的構造函數建立一個新的執行個體;如果有,就給這個執行個體的成員重新指派。如果需要,也可以将類設計為不可變的,這樣做的好處之一就是該類的執行個體是線程安全的。總之,不要在程式裡不必要的丢棄任何對象。

2.1.2 減少線程間的切換次數

    在J2ME遊戲的開發中,推薦使用單線程驅動的結構。由于Java在語言層面上提供了線程級的支援,是以相對而言,使用Java語言可以更容易地開發出多線程應用。但是由于線程切換通常是一個代價高昂地操作,是以在設計階段要盡量避免不必要的線程切換。例如線程間的同步通信,如果一個線程以同步的方式與另外一個線程通信,那麼第一個線程在發出消息之後,排程程式就必須讓第一個線程進入休眠狀态(在單處理器的情況下),同時喚醒第二個線程處理消息。是以大量的線程間同步通信對程式的性能有很大的沖擊。在J2ME遊戲的開發中,單線程驅動的結構通常可以達到良好的效果,這樣的結構既減少了線程切換的次數,又避免的棘手的線程間同步的問題。如果需要實作特殊的功能,可以通過定制特定的定時器(Timer)的方法解決。

2.1.3 減少基本資料類型間的強制類型轉換

    由于J2ME裝置的運作時記憶體有限,是以J2ME應用都傾向于的使用更精确的資料類型來描述資料。例如如果可以用byte型來定義變量就不使用int型。但是這樣做通常會導緻程式中有大量的基本資料類型之間的強制轉換。就像永遠不要過早優化一樣,這種對更精确的資料類型的使用也不是靈丹妙藥,應該分析程式的具體特點。通常應該對那些在程式中使用比較頻繁而且使用量較大的對象進行優化。例如如果在程式裡需要建立500個Building對象,那麼就可能需要對Building類進行優化,将Building的成員變量定義為更準确的資料類型。在一般情況下,還是應該使用預設int型。這樣會大大減少基本資料類型間的強制類型轉換對程式性能的沖擊。

2.1.4 盡量減少重畫的次數,如果可能,采用局部重畫技術

    就我個人的經驗而言,在J2ME遊戲中,背景圖的重新整理占用了很大一部分CPU資源。特别是在那種用圖塊(Tile)拼接出整個背景的程式(比如BomberMan和荒島探寶遊戲)中,這個現象比較明顯。例如在128×128的螢幕上,如果用12×12的圖塊拼接出整個背景,那麼需要做11×11=121次繪圖操作。如果采用全屏重新整理的政策,那麼代價就比較可觀了。常用的解決方案是:1、如果背景圖不可變,例如雷鳥号遊戲(背景圖隻是循環向下移動,其内容不變),那麼強烈推薦采用雙緩沖(隻是雙緩沖背景圖)技術。具體做法就是将圖塊繪制到一張記憶體圖檔中。在每次重畫背景時,将這張記憶體圖檔繪制到螢幕上,而不是用每次都用圖塊拼接出背景。如果記憶體情況允許的話,采用這種做法會顯著地提升性能;2、如果背景圖可變,仍然可以采用雙緩沖技術,但是會比較複雜。程式必須能夠識别髒圖塊(即已經改變而需要重畫的圖塊),如果不能的話,就失去了雙緩沖的意義。