设计一个简单的,易用的通信框架应该包含三个部分:监听器(耦合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; }