设计一个简单的,易用的通信框架应该包含三个部分:监听器(耦合UI线程和通信线程),计时器(在通信超时的情况下,通知UI线程),通信体(维护通信)。
下面给出一个设计完好的例子:
UI代码:
package app.midlet;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.TextBox;
import javax.microedition.midlet.MIDlet;
import app.http.HttpConst;
import app.http.Request;
import app.http.Response;
import app.http.UI_HTTP;
public class eastmoney extends MIDlet implements CommandListener {
private Command exitCommand = null;
private Display display = null;
private TextBox t = null;
private UI_HTTP http;
public eastmoney() {
init();
send();
exitCommand = new Command("Exit", Command.EXIT, 2);
t = new TextBox("welcome ", "", 256, 0);
t.addCommand(exitCommand);
t.setCommandListener(this);
}
private void init() {
http = new UI_HTTP();
}
public void startApp() {
display = Display.getDisplay(this);
display.setCurrent(t);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command arg0, Displayable arg1) {
if (arg0 == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}
private void send() {
Request request = new Request("cftpopup.eastmoney.com/mobile_toolbar_utf8.txt", null, null, HttpConst.COMM_AD);
request.method = HttpConnection.GET;
sendRequest(request);
}
public void sendRequest(Request request) {
http.sendRequest(request, this);
}
public void httpCompleted(Response resp) {
t.setString((String) resp.result);
}
public void httpException(Exception e) {
System.out.println(e);
}
}
请求的封装类:
package app.http;
import java.util.Hashtable;
import javax.microedition.io.HttpConnection;
/**
* 请求的封装类
*
* @author 冯小卫2011-12-9
*/
public class Request {
public String url;
public Hashtable properties;
public byte[] content;
public int commId;
// public int screenId;
public String method = HttpConnection.POST;
public Request() {
}
/**
* 适合普通POST请求
*
* @param url
* @param properties
* @param content
* @param commId
*/
public Request(String url, Hashtable properties, String content, int commId) {
this.url = url;
this.properties = properties;
if (content != null) {
this.content = content.getBytes();
}
this.commId = commId;
// this.screenId = screenId;
}
/**
* 适合普通GET请求
*
* @param url
* @param commId
* @param method
*/
public Request(String url, int commId, String method) {
this.url = url;
this.commId = commId;
// this.screenId = screenId;
this.method = method;
}
}
响应的封装类:
package app.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.io.HttpConnection;
/**
* 响应:包含响应结果
*
* @author 冯小卫2011-12-12
*/
public class Response {
public Object result;
public int commId;
// public int screenId;
/**
* 把响应数据填充到result中
*
* @param conn
* @param inputStream
* @param request
* @throws IOException
*/
public void setData(HttpConnection conn, InputStream inputStream, Request request) throws IOException {
byte[] buffer = new byte[HttpConst.BUFFER_SIZE];
int count;
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
while ((count = inputStream.read(buffer)) != -1) {
tmp.write(buffer, 0, count);
}
result = new String(tmp.toByteArray(), "UTF-8");
commId = request.commId;
}
}
监听器极其实现类:
package app.http;
import app.http.Response;
public interface HttpListener {
public void completed(Response resp);
public void exception(Exception e);
}
package app.http;
import javax.microedition.midlet.MIDlet;
import app.midlet.eastmoney;
/**
* UI线程和HTTP通信线程交互的类
*
* @author 冯小卫2011-12-9
*/
public class UI_HTTP implements HttpListener {
private HttpHandler httpHandler = null;
// private int[] requestInfo = null;
private MIDlet midlet;
public UI_HTTP() {
httpHandler = new HttpHandler(this);
}
/**
* 数据解析完成的时候,应该让界面刷新(没有处理)
*/
public void completed(Response resp) {
try {
// if (requestInfo != null) {
// BaseScreen requestScreen = findRequestScreen();
// if (requestScreen != null) {
// requestScreen.httpCompleted(resp);
// }
// }
((eastmoney) midlet).httpCompleted(resp);
} catch (Exception e) {
e.printStackTrace();
}
// @完成Http通讯后刷新normal controls
// this.setNeedPaint();
}
public void exception(Exception e) {
((eastmoney) midlet).httpException(e);
}
public void timeOut() {
((eastmoney) midlet).httpException(new Exception("timeOut"));
}
public void sendRequest(Request request) {
// requestInfo = new int[] { request.screenId, request.commId };
httpHandler.sendRequest(request);
}
/**
* @param request
*/
public void sendRequest(Request request, MIDlet midlet) {
// requestInfo = new int[] { request.screenId, request.commId };
this.midlet = midlet;
httpHandler.sendRequest(request);
}
/**
* 停止HTTP请求
*/
public void stop() {
httpHandler.stop();
}
}
计时器:
package app.http;
public class HttpMonitor implements Runnable {
// 超时时间(毫秒)
long timeout;
long startTime;
boolean active = false;
boolean time = false;
HttpHandler handler;
public HttpMonitor(HttpHandler handler) {
this.handler = handler;
active = true;
timeout = 10 * 1000;// 10秒认为超时
}
public void run() {
while (active) {
if (time) {
long tmp = System.currentTimeMillis() - startTime;
if (tmp > timeout) {
handler.timeout();
stopTimer();
} else {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
active = false;
}
}
}
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
active = false;
}
}
}
public void startTimer() {
if (!time) {
startTime = System.currentTimeMillis();
time = true;
}
}
/**
* 停止本次计时,但是计时线程依然运行
*/
public void stopTimer() {
time = false;
}
/**
* 终止计时线程
*/
public void cleanup() {
time = false;
active = false;
}
}
通信体:
package app.http;
import java.util.Vector;
public class HttpHandler implements Runnable {
private HttpMonitor httpMonitor;
private boolean isRunning = false;
// Http client
private HttpClient httpClient = null;
// HttpListener
private HttpListener httpListener = null;
// 等待发送的缓冲请求信息
private Vector vctRequests = new Vector();
public HttpHandler(HttpListener httpListener) {
this.httpListener = httpListener;
httpClient = new HttpClient();
isRunning = true;
new Thread(this).start();// 启动发送线程
}
public void run() {
while (isRunning) {
try {
if (vctRequests.size() > 0) {
// 得到Request
Request request = null;
synchronized (vctRequests) {
if (vctRequests.size() > 0) {
request = (Request) vctRequests.elementAt(0);
vctRequests.removeElementAt(0);
}
}
// 通过网络请求得到数据
Response resp = getResponse(request);
// 数据到达以后,送给界面解析
if (resp != null) {
if (httpListener != null) {
httpListener.completed(resp);
}
resp = null;
}
}
Thread.sleep(200);
} catch (Exception e) {
}
}
}
private void handleException(Exception e) {
if (httpListener != null) {
httpListener.exception(e);
}
}
public void sendRequest(Request request) {
if (request == null) {
return;
}
synchronized (vctRequests) {
vctRequests.addElement(request);
}
}
public Response getResponse(Request request) {
Response resp = null;
try {
// 处理数据过程可能比较长 不建议放在synchronized域中
startTimer();
resp = httpClient.getResponse(request);
stopTimer();
} catch (Exception e) {
stopTimer();
handleException(e);
}
return resp;
}
private void startTimer() {
httpMonitor = getRequestTimer();
httpMonitor.startTimer();
}
private HttpMonitor getRequestTimer() {
if (httpMonitor == null) {
httpMonitor = new HttpMonitor(this);
new Thread(httpMonitor).start();
}
return httpMonitor;
}
private void stopTimer() {
if (httpMonitor != null) {
httpMonitor.stopTimer();
}
}
/**
* 停止HTTP请求
*/
public void stop() {
httpMonitor.cleanup();
// 请空缓存请求数据
synchronized (vctRequests) {
vctRequests.removeAllElements();
}
httpListener = null;
// 中断数据接收监听
isRunning = false;
httpClient = null;
}
// 这里通信超时的处理时停止计时器,然后通知UI线程
public void timeout() { // 数据接收超时
httpMonitor.stopTimer();
httpListener.exception(new Exception("timeOut"));
}
}
package app.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import app.utils.Functions;
public class HttpClient {
private HttpConnection conn = null;
private InputStream inputStream = null;
private OutputStream outputStream = null;
private Request request = null;
/**
* 应该在这里处理资费页面的,但是还没有处理
*
* @param request
* @param httpHandler
* @return
* @throws Exception
*/
public Response getResponse(Request request) throws Exception {
if (request == null) {
return null;
}
try {
this.request = request;
Response resp = new Response();
makeConnection(request.url);
setProperty();
setContent();
if (conn.getResponseCode() != HttpConnection.HTTP_OK) {
resp.result = "BAD" + "-" + conn.getResponseCode();
return resp;
} else {
makeInputStream();
resp.setData(conn, inputStream, request);
}
return resp;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
this.request = null;
cleanup();
}
}
private void cleanup() {
try {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
if (outputStream != null) {
outputStream.close();
outputStream = null;
}
if (conn != null) {
conn.close();
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void makeInputStream() throws Exception {
inputStream = conn.openInputStream();
}
private void setContent() throws IOException {
if (request.content != null) {// post
if (conn.getRequestProperty("Content-Type") == null) {
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
}
conn.setRequestProperty("Content-Length", String.valueOf(request.content.length));
makeOutputStream();
outputStream.write(request.content);
// outputStream.flush();//不要使用,会导致编码变成 chunked
} else {// get
makeOutputStream();
}
}
private void makeOutputStream() throws IOException {
outputStream = conn.openOutputStream();
}
/**
* 设置请求方式和参数信息
*
* @throws IOException
*/
private void setProperty() throws IOException {
conn.setRequestMethod(request.method);
Hashtable table = request.properties;
if (table != null) {
Enumeration keys = table.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = (String) table.get(key);
conn.setRequestProperty(key, value);
}
}
}
/**
* 打开连接,可能发生IOException
*
* @param url
* @throws IOException
*/
private void makeConnection(String url) throws IOException {
if (HttpConst.IS_NET) {
conn = (HttpConnection) Connector.open("http://" + url);
} else {
conn = (HttpConnection) Connector.open("http://" + HttpConst.HTTP_CMWAP + Functions.getNoHost(url));
conn.setRequestProperty("X-Online-Host", Functions.getHost(url));
}
}
}
用到的工具方法和常量:
package app.utils;
public class Functions {
/**
* 获取一个url的host(url指除去http://后剩下的部分)
*
* @param url
* @return
*/
public static String getHost(String url) {
if (url == null) {
return null;
}
if (url.indexOf("/") > 0) {
return url.substring(0, url.indexOf("/"));
}
return url;
}
/**
* 获取一个url除去host的部分(url指除去http://后剩下的部分)
*
* @param url
* @return
*/
public static String getNoHost(String url) {
if (url == null) {
return null;
}
if (url.indexOf("/") > 0) {
return url.substring(url.indexOf("/") + 1);
}
return "index.html";
}
}
package app.http;
public interface HttpConst {
/**
* 默认缓冲区大小
*/
int BUFFER_SIZE = 1024;
/**
* 联网方式(默认cmnet)
*/
boolean IS_NET = true;
/**
* 默认的代理服务器
*/
String HTTP_CMWAP = "10.0.0.172:80/";
// commId
int COMM_AD = 1;
}