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