当下载完毕后,HttpWaitUI就获得了来自服务器的数据,要传递给下一个屏幕处理,HttpWaitUI必须包含对此屏幕的引用并通过一个setData(DataInputStream input)方法让下一个屏幕能非常方便地读取数据。因此,定义一个DataHandler接口:
public interface DataHandler {
void setData(DataInputStream input) throws IOException;
}
HttpWaitUI响应HttpThread的onFinish事件并调用下一个屏幕的setData方法将数据传递给它并显示下一个屏幕:
public void onFinish(byte[] buffer, int size) {
byte[] data = buffer;
if(size!=buffer.length) {
data = new byte[size];
System.arraycopy(data, 0, buffer, 0, size);
}
DataInputStream input = null;
try {
input = new DataInputStream(new ByteArrayInputStream(data));
if(displayable instanceof DataHandler)
((DataHandler)displayable).setData(input);
else
System.err.println("[WARNING] Displayable object cannot handle data.");
ControllerMIDlet.replace(displayable);
}
catch(IOException ioe) { … }
}
以下载一则新闻为例,一个完整的HTTP GET请求过程如下:
首先,用户通过点击某个屏幕的命令希望阅读指定的一则新闻,在commandAction事件中,我们初始化HttpWaitUI和显示数据的NewsUI屏幕:
public void commandAction(Command c, Displayable d) {
HttpWaitUI wait = new HttpWaitUI("http://192.168.0.1/news.do?id=1", new NewsUI());
ControllerMIDlet.forward(wait);
}
NewsUI实现DataHandler接口并负责显示下载的数据:
public class NewsUI extends Form implements DataHandler {
public void setData(DataInputStream input) throws IOException {
String title = input.readUTF();
Date date = new Date(input.readLong());
String text = input.readUTF();
append(new StringItem("Title", title));
append(new StringItem("Date", date.toString()));
append(text);
}
}
服务器端只要以String, long, String的顺序依次写入DataOutputStream,MIDP客户端就可以通过DataInputStream依次取得相应的数据,完全不需要解析XML之类的文本,非常高效而且方便。
需要获得联网数据的屏幕只需实现DataHandler接口,并向HttpWaitUI传入一个URL即可复用上述代码,无须关心如何连接网络以及如何处理用户中断连接。
使用POST发送数据
以POST方式发送数据主要是为了向服务器发送较大量的客户端的数据,它不受URL的长度限制。POST请求将数据以URL编码的形式放在HTTP正文中,字段形式为fieldname=value,用&分隔每个字段。注意所有的字段都被作为字符串处理。实际上我们要做的就是模拟浏览器POST一个表单。以下是IE发送一个登陆表单的POST请求:
POST http://127.0.0.1/login.do HTTP/1.0
Accept: image/gif, image/jpeg, image/pjpeg, */*
Accept-Language: en-us,zh-cn;q=0.5
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Length: 28
\r\n
username=admin&password=1234
要在MIDP应用程序中模拟浏览器发送这个POST请求,首先设置HttpConnection的请求方式为POST:
hc.setRequestMethod(HttpConnection.POST);
然后构造出HTTP正文:
byte[] data = "username=admin&password=1234".getBytes();
并计算正文长度,填入Content-Type和Content-Length:
hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
hc.setRequestProperty("Content-Length", String.valueOf(data.length));
然后打开OutputStream将正文写入:
OutputStream output = hc.openOutputStream();
output.write(data);
需要注意的是,数据仍需要以URL编码格式编码,由于MIDP库中没有J2SE中与之对应的URLEncoder类,因此,需要自己动手编写这个encode()方法,可以参考java.net.URLEncoder.java的源码。剩下的便是读取服务器响应,代码与GET一致,这里就不再详述。
使用multipart/form-data发送文件
如果要在MIDP客户端向服务器上传文件,我们就必须模拟一个POST multipart/form-data类型的请求,Content-Type必须是multipart/form-data。
以multipart/form-data编码的POST请求格式与application/x-www-form-urlencoded完全不同,multipart/form-data需要首先在HTTP请求头设置一个分隔符,例如ABCD:
hc.setRequestProperty("Content-Type", "multipart/form-data; boundary=ABCD");
然后,将每个字段用“--分隔符”分隔,最后一个“--分隔符--”表示结束。例如,要上传一个title字段"Today"和一个文件C:\1.txt,HTTP正文如下:
--ABCD
Content-Disposition: form-data; name="title"
\r\n
Today
--ABCD
Content-Disposition: form-data; name="1.txt"; filename="C:\1.txt"
Content-Type: text/plain
\r\n
<这里是1.txt文件的内容>
--ABCD--
\r\n
请注意,每一行都必须以\r\n结束,包括最后一行。如果用Sniffer程序检测IE发送的POST请求,可以发现IE的分隔符类似于---------------------------7d4a6d158c9,这是IE产生的一个随机数,目的是防止上传文件中出现分隔符导致服务器无法正确识别文件起始位置。我们可以写一个固定的分隔符,只要足够复杂即可。
发送文件的POST代码如下: