隨著國內(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 }
現(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 }