随着国内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);
}
}
/***********************************************************/