小議J2ME手機(jī)游戲引擎程序架構(gòu)

字號:

隨著國內(nèi)3G的啟動,新一代移動通信大潮已經(jīng)到來。技術(shù)的進(jìn)步使得無線網(wǎng)絡(luò)取得不錯的發(fā)展,移動互聯(lián)網(wǎng)巨大前景也隨著顯現(xiàn)。無線網(wǎng)絡(luò)速度的提高,催生大量的手機(jī)聯(lián)網(wǎng)應(yīng)用程序。手機(jī)聯(lián)網(wǎng)功能的強(qiáng)化,使得手機(jī)應(yīng)用更具價值,進(jìn)一步擴(kuò)展了手機(jī)功能。
    現(xiàn)在我們就來實現(xiàn)一個基于J2ME的手機(jī)聯(lián)網(wǎng)程序。考慮到手機(jī)運(yùn)算資源的限制,我們采用客戶端/服務(wù)器 target=_blank>服務(wù)器的模式來實現(xiàn),J2ME只做為客戶端運(yùn)行于手機(jī)上,負(fù)責(zé)展現(xiàn)和處理簡單的業(yè)務(wù)邏輯,保存少量的關(guān)鍵數(shù)據(jù);服務(wù)器端采用J2EE實現(xiàn),負(fù)責(zé)保存用戶數(shù)據(jù),以及響應(yīng)在線用戶的復(fù)雜業(yè)務(wù)邏輯。
    在這里,服務(wù)端J2EE的實現(xiàn)不是本文的重點(diǎn),所以只進(jìn)行簡單的描述,我們主要對手機(jī)客戶端J2ME的講解。
    在J2ME客戶端,我們可以采用的MVC軟件架構(gòu)模式,進(jìn)行邏輯分層,使代碼更為清晰,權(quán)責(zé)更為明確,也利于代碼維護(hù)和功能升級。
    1、Handle(Controller):它既做為控制器,也做為簡單邏輯處理器。如處理網(wǎng)絡(luò)請求,網(wǎng)絡(luò)消息分發(fā)。它是關(guān)鍵層,涉及到整體結(jié)構(gòu)的每一層,用于控制應(yīng)用程序的流程。它處理事件并作出響應(yīng)。因為復(fù)雜業(yè)務(wù)邏輯的處理權(quán)責(zé)已經(jīng)分化到服務(wù)器端,只處理簡單的邏輯和少量的數(shù)據(jù)訪問,所以此層沒有進(jìn)一步劃分出業(yè)務(wù)層,統(tǒng)一劃為處理層。
    2、DAO:數(shù)據(jù)訪問對象(Data Access Object),用于封裝數(shù)據(jù)的于Database的讀取和存儲 target=_blank>存儲操作。便于Handle的調(diào)用完成簡單業(yè)務(wù)邏輯的處理。
    3、Database:用來存儲少量數(shù)據(jù),即負(fù)責(zé)關(guān)鍵數(shù)據(jù)的持久化。在J2ME中,RMS(Record Management System)是這個層次主要承擔(dān)者。在實際應(yīng)用中,如果數(shù)據(jù)間關(guān)系很簡單,也可以選用文件進(jìn)行保存,如XML格式或普通文本格式。Handler會控制對Database的存儲和提取,用來View層顯示。
    4、Model:數(shù)據(jù)模型用于封裝與應(yīng)用程序的業(yè)務(wù)邏輯相關(guān)的數(shù)據(jù)以及對數(shù)據(jù)的處理方法。數(shù)據(jù)的抽象化分離了具體的View,也方便Handle對數(shù)據(jù)持久化操作。
    5、View: 這層用來顯示用戶界面,并且響應(yīng)和處理鍵盤的指令。將Handler層指派的一些信息顯示出來,并且將需求信息送給Handler去處理。所以這層直接于Handler溝通,不會直接涉及到Database或網(wǎng)絡(luò)信息。
    我們這里做的這個聯(lián)網(wǎng)程序是一個即時讀取互聯(lián)網(wǎng)資訊的工具,從任意一個網(wǎng)站的資訊列表頁面獲取其中的資訊標(biāo)題和鏈接,返回給手機(jī)客戶端。而在手機(jī)客戶端選取一條資訊進(jìn)行打開時,又去聯(lián)網(wǎng)獲取文章的文本內(nèi)容。從服務(wù)器端返回的數(shù)據(jù)中只包含文本信息,無任何除資訊資訊URL的HTML數(shù)據(jù)。這樣就大大減少了無用數(shù)據(jù)傳輸,降低了網(wǎng)絡(luò)通信費(fèi)用,提高訪問速度,更為方便地訪問WWW網(wǎng)站。
    服務(wù)端要處理的業(yè)務(wù)邏輯有:根據(jù)提供的鏈接地址,獲取頁面內(nèi)容并進(jìn)行分析,提取資訊條目或資訊內(nèi)容數(shù)據(jù)。返回給手機(jī)客戶端。這部分的處理邏輯有一定的復(fù)雜,需要耗費(fèi)一定的資源,所以將其劃分到服務(wù)端進(jìn)行處理。
    客戶端只負(fù)責(zé)發(fā)送請求和接收服務(wù)端返回的數(shù)據(jù),進(jìn)行簡單處理后將內(nèi)容呈現(xiàn)到用戶瀏覽界面。相當(dāng)于一個網(wǎng)頁瀏覽器。
    1 package com.efan.wb.view;
    2
    3 import javax.microedition.lcdui.Command;
    4 import javax.microedition.lcdui.CommandListener;
    5 import javax.microedition.lcdui.Display;
    6 import javax.microedition.lcdui.Displayable;
    7 import javax.microedition.lcdui.Form;
    8 import javax.microedition.lcdui.TextBox;
    9 import javax.microedition.midlet.MIDlet;
    10 import com.efan.wb.handle.WbAction;
    11
    12 public class WebBrowser extends MIDlet implements CommandListener {
    13
    14 private TextBox textbox;
    15 private Display display = null;
    16 private Form mainForm = null;
    17 public static final Command exitCommand = new Command("Exit", Command.OK, 1);
    18
    19 public void startApp() {
    20 Display.getDisplay(this).setCurrent(textbox);
    21 if (display == null) {
    22 display = Display.getDisplay(this);
    23 }
    24 mainForm = new Form("News Form");
    25
    26 // 從控制器加載
    27 WbAction action = new WbAction();
    28 String newsList = action.getNews();
    29
    30 mainForm.append(newsList);// 加載默認(rèn)新聞標(biāo)題列表
    31 mainForm.addCommand(exitCommand);
    32 mainForm.setCommandListener(this);
    33 display.setCurrent(mainForm);
    34 }
    35
    36 public void commandAction(Command cmd, Displayable displayable) {
    37 if (cmd == exitCommand) {
    38 destroyApp(false);
    39 notifyDestroyed();
    40 }
    41 }
    42
    43 public void pauseApp() {
    44 }
    45
    46 public void destroyApp(boolean unconditional) {
    47 }
    48 }
    49  1 package com.efan.wb.handle;
    2
    3 import java.io.DataInputStream;
    4 import java.io.IOException;
    5
    6 import javax.microedition.io.Connector;
    7 import javax.microedition.io.HttpConnection;
    8 import com.efan.wb.dao.WbDao;
    9 import com.efan.wb.model.UrlEntity;
    10
    11 public class WbAction {
    12
    13 public String getNews() {
    14
    15 // 從RMS中獲取默認(rèn)的URL
    16 WbDao dao = new WbDao();
    17 UrlEntity ue = dao.getDefaultURL();
    18 String url = ue.getUrl();
    19
    20 WebExplorer we = new WebExplorer(url);
    21 we.start();// 啟動網(wǎng)絡(luò)新聞獲取線程
    22
    23 // 輪循等待操作完成
    24 while (!we.isComplete()) {
    25 // 超時處理,此略
    26 }
    27 return we.getNewsList();
    28 }
    29
    30 // 為了簡化代碼,把這個訪問網(wǎng)絡(luò)的線程類作為內(nèi)部類
    31 class WebExplorer extends Thread {
    32
    33 private String newsList;
    34
    35 private String url;
    36
    37 public WebExplorer(String url) {
    38 this.url = url;
    39 }
    40
    41 public WebExplorer() {
    42 }
    43
    44 public void setUrl(String url) {
    45 this.url = url;
    46 }
    47
    48 private boolean isComplete() {
    49 return this.newsList == null ? false : true;
    50 }
    51
    52 public String getNewsList() {
    53 return newsList;
    54 }
    55
    56 public void run() {
    57 try {
    58 HttpConnection conn = (HttpConnection) Connector.open(this.url);
    59 DataInputStream is = conn.openDataInputStream();
    60 this.newsList = is.readUTF();
    61 } catch (IOException e) {
    62 e.printStackTrace();
    63 }
    64 }
    65 }
    66 }
    67 1 package com.efan.wb.dao;
    2
    3 import com.efan.wb.model.UrlEntity;
    4
    5 public class WbDao {
    6
    7 public UrlEntity getDefaultURL() {
    8
    9 UrlEntity ue = new UrlEntity();
    10 // 訪問RMS,查詢默認(rèn)的URL,此略,直接硬編碼來測試
    11 String url = "http://localhost:8080/rss/news";
    12 String name = "Test Web URL";
    13
    14 ue.setName(name);
    15 ue.setUrl(url);
    16
    17 return ue;
    18 }
    19 }
    1 package com.efan.wb.model;
    2
    3 public class UrlEntity {
    4
    5 private String name;
    6
    7 private String url;
    8
    9 public String getName() {
    10 return name;
    11 }
    12
    13 public void setName(String name) {
    14 this.name = name;
    15 }
    16
    17 public String getUrl() {
    18 return url;
    19 }
    20
    21 public void setUrl(String url) {
    22 this.url = url;
    23 }
    24
    25 }
    1 package cn.rssweb.site.web;
    2
    3 import java.io.DataOutputStream;
    4 import java.io.IOException;
    5 import java.util.Iterator;
    6 import java.util.List;
    7
    8 import javax.servlet.ServletException;
    9 import javax.servlet.http.HttpServlet;
    10 import javax.servlet.http.HttpServletRequest;
    11 import javax.servlet.http.HttpServletResponse;
    12
    13 import cn.rssweb.edp.component.spider.RobotFactory;
    14 import cn.rssweb.edp.component.spider.model.DataModel;
    15 import cn.rssweb.edp.component.spider.robot.Robot;
    16
    17 public class NewsListServlet extends HttpServlet {
    18
    19 @Override
    20 protected void doGet(HttpServletRequest request, HttpServletResponse response)
    21 throws ServletException, IOException {
    22
    23 String url = "http://www.techweb.com.cn/news/20.shtml";
    24
    25 Robot robot = RobotFactory.getInstance(Robot.HTML,url);//服務(wù)器邏輯處理核心類,此略
    26 List list = robot.parseList();
    27
    28 Iterator it = list.iterator();
    29 int i=0;
    30 StringBuffer sb = new StringBuffer();
    31 while(it.hasNext()){
    32 i++;
    33 DataModel data = (DataModel)it.next();
    34 String title = data.getLinkText();
    35 sb.append(i).append(".").append(title).append("\n");
    36 }
    37
    38 DataOutputStream dos = new DataOutputStream(response.getOutputStream());
    39
    40 dos.writeUTF(sb.toString());
    41 dos.flush();
    42 dos.close();
    43
    44 }
    45
    46 @Override
    47 protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1)
    48 throws ServletException, IOException {
    49
    50 super.doPost(arg0, arg1);
    51 }
    52
    53 }