只有你实现移动设备与非移动设备互相通信的功能后,你设计的移动设备应用程序才可以称的上是真正有用的。手持PC如Palm拥有同步功能,使得它们可以实现上述功能,但是其它设备,如Java嵌入式电话,该如何实现该功能呢?J2ME的MIDP在javax.microedition.io程序包中提供了一整套类,统称为通用连接构架(Generic Connection Framework ,GCF)。GCF使得与其它设备的可编程传输(programmatically transferring)数据变得非常容易。
与世界相连
GCF是建立在用连接类来管理通信的概念基础上的,每个类用来管理具体某种通信方法。抽象Connection(通信)类(实际上,它只有一个已定义的方法,所以它确实很抽象)是所有连接连接类的基类,它可以依次实现五个接口类中的一种来定义他们的功能。这种设计的意图就是使得GCF变得通用而且易于扩展,而且使得编程者用起来尽可能的简单。
MIDP 1.0唯一支持的连接类型就是HTTP了,HTTP协议用于Web页面服务。对HTTP协议的支持是在HttpConnection类中实现的,我将在本文集中阐述这个类。你也许觉得内容限制在HTTP上不太好,实际上并不是这样的。你的移动设备应用程序最终将可以和任意一种Web服务器通信,并且HttpConnection类会很好的把各种管理通信的行为抽象出来:你无需担心端口或者其它乱七八糟的东西。你只需要知道三种HTTP请求,并把它们作为你的应用程序的连接媒介。
HTTP简述
详尽、透彻的讨论HTTP结构将会超出了本文的范围,可以这么说HTTP客户端可以向服务器端发出三种不同的请求中的某一种:GET、POST或者HEAD。不同请求的意义各不相同:
GET请求是用来请求数据的,如Web页面或者多媒体文件。你也可以通过URL用GET请求向服务器发送数据,不过用这种方式只可以发送少量的数据,很显然,嵌入到URL的数据是清晰易见的(plainly visible)。
POST请求可以通过单独的数据流来向URL发送数据。提交Web表单(form)会使你的浏览器向服务器发送POST请求。然后,Web服务器把检索到数据发送给页面或者URL指定的CGI程序中。在POST请求中你可以发送比GET请求更多的数据,不过它并不是真正安全可靠的,至少你的数据不是清晰的(in plain sight)。
最后要说说HEAD请求,它用来在服务器上检索元数据(metadata)。服务器的回应并不包含实际请求的数据,包含的只是关于请求的信息。
当收到客户端发出的请求后,服务器发出回应。回应信息可分为信息头和正文两个部分,它们包含实际请求的数据。信息头包括用来表示请求是否成功的状态字。状态字200表示请求成功,状态字400或者400以上表示请求失败。
使用HttpConnection类
现在,我们完全基于HTTP来看看如何用HttpConnection类来请求远端的服务器并获取数据。你可以用静态Connector类打开HttpConnection,它会为你维护对象。打开连接后,你可以通过指定HttpConnection.GET、 HttpConnection.POST,、或者HttpConnection.HEAD来设置你希望使用的客户请求类型:
http = (HttpConnection) Connector.open(“http://builder.com.com”);
http.setRequestMethod(HttpConnection.GET);
你指定的请求会自动发送到你在Connector.open中设定的URL。然后你可以通过HttpConnection类中的getResponseCode方法来检测请求是否成功
if (http.getResponseCode() == HttpConnection.HTTP_OK){
//成功!
} else {
//失败
}
实际上,一旦你建立连接,发送和接受数据就是很简单的一件事了,你只需用HttpConnection类的openInputStream和openOutputStream方法打开适当的流对象就可以了。
接收数据
让我们看一个具体的例子。在程序清单A中,我已经写好了用以建立一个名为CGFMidlet的MIDlet例子的代码,它演示了通过HttpConnection类,如何读取保存在Web服务器中的小文本文件内容的过程。在运行这个例子前,你有可能需要修改URL(http://你的Web服务器/你的文件名.txt)为合适的内容。
让我们看看getHTTPFile方法,它是用来处理文件检索的。你就会发现HttpConnection类为该文件发出GET请求。当检查回应状态字、确保没有问题后,我用InputStream方法读取文件的内容并把它显示在MIDlet的主表单中:
//打开读取结果的输入流
stream = http.openInputStream();
byte data[] = new byte[(int) http.getLength()];
stream.read(data);
//把结果显示在主表单的输出字段上
output.setString(new String(data));
发送数据
你也可以用GET请求来向服务器发送数据,但是这个方法有个缺点(只能向服务器发送少量数据),我在前面已经暗示了这一点。如果发送数据的量很大,你最好用POST请求。你可以在程序清单B中看到ServerConnetion类是如何做到这一点的。该程序清单是我在本系列文章中所用到的例子程序ExpensesApp(你可以在这儿下载源代码)可以把开销发送到服务器保存而写的代码。除了我现在用HttpConnection.POST做为请求类型以及设置一种特殊的信息头来帮助服务器处理请求中的数据外,其它的东西都大致一样:
http.setRequestMethod(HttpConnection.POST);
//必须设置内容类型t
http.setRequestProperty("Content-Type",_
"application/x-www-form-urlencoded");
//为打开的连接打开输出流
OutputStream ostream = http.openOutputStream()
然后,我循环访问ExpenseInfo数组,并把数组中的各个对象联为一个字符串,用OutputStream方法把字符串所保存的数据发送到服务器:
for (int i=0; i
String s = ("ExpenseDate=" + expensesRecords[i].getDate().toString()+
"&ExpenseCategory=" + expensesRecords[i].getCategory() +
"&ExpenseAmount=" + String.valueOf(expensesRecords[i].getDollars()) +
"." + String.valueOf(expensesRecords[i].getCents()) +
"&ExpenseDescription=" + expensesRecords[i].getDescription());
在服务器端,你需要类似于CGI类型的处理过程(例如ASP、JSP或者Servlet)来接受数据。字符串中的每一对“标识符——数值”将按照递交表单的控制显示在接受页上,所以你的接受页访问这些数据的方式和访问表单数据相同。
尽管HTTP并不是传输大量数据的理想工具,它的灵活性还是足以用来完成本文中的工作,并且GCF使HTTP的用法特别简单。很幸运,GCF以后的版本将为开发者提供除HTTP外更多的选择。
本文相关连接请点这里