隨著越來越多手提電話和個人數(shù)字助理開始融入到信息高速公路之上,從移動設(shè)備上訪問Web站點(diǎn)變得越來越重要。java開創(chuàng)了消費(fèi)設(shè)備中小型的儲存容量的先河,它是用于開發(fā)手機(jī)、傳呼機(jī)及其他微型設(shè)備應(yīng)用程序的理想語言。
在本文中,我們將學(xué)習(xí)如何從一個J2ME客戶機(jī)上向服務(wù)器發(fā)送一條HTTPGET請求和一條HTTPPOST請求。雖然這只是一篇探討性質(zhì)的文章,但是我還是假定讀者已經(jīng)熟悉Java,J2ME,以及JavaMidlets(MIDP應(yīng)用程序)的運(yùn)作機(jī)制。我們將使用J2ME的MIDP簡表,并利用SUN的J2ME的無線應(yīng)用程序開發(fā)工具包編譯、配置和測試我們的應(yīng)用程序。對于HTTP服務(wù)器,任何WWW地址都可以被訪問,但是默認(rèn)時我們將使用一個簡單的JavaServlet來返回我們的HTTP請求的細(xì)節(jié)。
如何使用J2ME客戶機(jī)向Web服務(wù)器和類似的支持HTTP的服務(wù)器發(fā)送HTTP請求呢?答案就是使用可在javax.microedition.io程序包中可找到的J2ME的網(wǎng)絡(luò)類。本文就想具體闡述這個問題。
本文概述∶
使用J2ME設(shè)計(jì)無線網(wǎng)絡(luò)應(yīng)用程序
.發(fā)送一條超文本GET請求
.發(fā)送一條超文本POST請求
.使用J2ME進(jìn)行無線網(wǎng)絡(luò)編程
Java的網(wǎng)絡(luò)編程能力是相當(dāng)健壯的。Java2標(biāo)準(zhǔn)版(J2SE)在java.io和java.net程序包中定義了100多個接口程序,類和異常。通過這些庫實(shí)現(xiàn)的功能是很強(qiáng)大的,但是這只適用于傳統(tǒng)的計(jì)算機(jī)系統(tǒng),這些計(jì)算機(jī)系統(tǒng)有強(qiáng)大的CPU處理能力,快速的內(nèi)存和持久的數(shù)據(jù)儲存,但是這些在大多數(shù)的無線設(shè)備上是不現(xiàn)實(shí)的。因此,J2ME定義了這些函數(shù)的子集,并提供了一套用于網(wǎng)絡(luò)和文件訪問的固定的程序包---javax.microedition.io程序包。由于可移動設(shè)備種類繁多,這個程序包僅僅定義了一套接口,而為每個可移動設(shè)備供給廠商留下了實(shí)際的應(yīng)用程序接口實(shí)現(xiàn)。這就在可移植性和設(shè)備特定特征的應(yīng)用中找到了一個最佳的平衡點(diǎn)。
定義在javax.microedition.io類中的抽象網(wǎng)絡(luò)和文件輸入輸出框架稱為通用連接框架(GenericConnectionFramework,簡稱GCF)。GCF定義了一套有關(guān)抽象化的內(nèi)容來描述不同的通信方法。最高級的抽象被稱作連接(Connection),還聲明了六個接口(四個是直接的,兩個是間接的)。這七個接口就構(gòu)成了J2ME的CLDC的一部分,CLDC是大多數(shù)的能使用Java的無線設(shè)備使用的配置。設(shè)計(jì)這個配置的目的就是為所有的CLDC設(shè)備(手提電話,雙向傳呼機(jī),低檔的PDA等等)提供公用的網(wǎng)絡(luò)和文件輸入輸出能力。雖然GCF的目的是公用網(wǎng)絡(luò)和文件輸入輸出框架,但是生產(chǎn)商并不要求實(shí)現(xiàn)GCF中聲明的所有的接口。有的廠家可以決定只支持socket連接,而其它的廠家可以選擇只支持基于數(shù)據(jù)報的通信。為了促進(jìn)跨越類似裝置的可移植性,MIDP規(guī)范要求所有的MIDP設(shè)備實(shí)現(xiàn)HttpConnection接口。HttpConnection不是GCF的一部分,但是它是從GCF的一個接口ContentConnection衍生出來的。我們將使用HttpConnection接口構(gòu)造我們樣本應(yīng)用程序。
發(fā)送一個HTTPGET請求
這一節(jié)將重點(diǎn)解釋程序代碼,在下一節(jié)中我們將只講述被用來發(fā)送HTTP請求并檢索由服務(wù)器返回的響應(yīng)通用連接框架接口和HttpConnection接口。創(chuàng)建MIDP用戶界面的程序代碼見附錄。
我們先要定義一個方法來放用于發(fā)送HTTPGET請求的代碼。因?yàn)檫@個方法中的有些操作有潛在的拋出IOException的可能,所以我們將把這樣的意外(exception)拋給調(diào)用方法。
publicStringsendHttpGet(Stringurl)throwsIOException{;
HttpConnectionhcon=null;
DataInputStreamdis=null;
StringBuffermessage="";
try{;
第一步是使用Connector類打開一個到服務(wù)器的連接,這是GCF的要害。我們將把這個連接強(qiáng)制轉(zhuǎn)換為需要的類型,在本例中為HttpConnection類型。
hcon=(HttpConnection)Connector.open(url);
接下來,我們得到HttpConnection上的一個DataInputStream,答應(yīng)我們一個字符一個字符的讀取服務(wù)器的響應(yīng)數(shù)據(jù)。
dis=newDataInputStream(hcon.openInputStream());
使用DataInputStream的read()方法,服務(wù)器響應(yīng)的每個字符都被集中起來放入StringBuffer對象。
intch;
while((ch=dis.read())!=-1){;
message=message.append((char)ch);
};
最后,連接對象被凈空以保存資源,而信息從這個方法中返回。
};finally{;
if(hcon!=null)hcon.close();
if(dis!=null)dis.close();
};//結(jié)束try/finally代碼段
returnmessage.toString();
};//結(jié)束sendGetRequest(String)
如何發(fā)送一個HTTPPOST請求
你可以想象,發(fā)送一個HTTPPOST請求的處理過程其實(shí)與發(fā)送一個GET請求非常地類似。我們將修改一個現(xiàn)有命令,添加少量的新的命令,并添加一個來自通用連接框架的附加的對象和一個附加的StringBuffer對象把POST請求體重的內(nèi)容發(fā)送到服務(wù)器中。剩下的命令將保持不變。
復(fù)制我們剛才創(chuàng)建的sendHttpGet()方法,把它粘貼進(jìn)同一個類文件,改名為sendHttpPost()。現(xiàn)在,我們將修改這個新方法來發(fā)送一個HTTPPOST請求到服務(wù)器。在方法的頂部添加兩個新的變量說明。聲明一個類型為DataOutputStream的變量和另一個String類型的變量。我們將使用DataOutputStream對象把存在于字符串變量中的POST請求體發(fā)送到服務(wù)器中。
DataOutputStreamdos=null;
StringrequestBody=null;
修改connector.open()命令包含另一個參數(shù),指出連接將答應(yīng)客戶端可以通過連接在服務(wù)器上讀和寫。
hcon=(HttpConnection)Connector.open(url,Connector.READ_WRITE);
設(shè)置HttpConnection對象使用的請求方法為POST(默認(rèn)的方法是GET)。
hcon.setRequestMethod(HttpConnection.POST);
得到一個用于現(xiàn)有的HTTP連接的DataOutputStream對象。
dos=hc.openDataOutputStream();
聲明一個字節(jié)數(shù)組并通過檢索一個來自requestBody字符串的字節(jié)數(shù)組初始化。然后把DataOutputStream的緩沖寫入字節(jié)數(shù)組內(nèi)。
byte[]byteRequest=requestBody.getBytes();
for(inti=0;i<byteRequest.length;i++){;
dos.writeByte(byteRequest[i]);
};//結(jié)束for(inti=0;i<byteRequest.length;i++)
dos.flush();//包含本句,在某些設(shè)被上將可能會產(chǎn)生不可預(yù)期的結(jié)果
調(diào)用flush()方法的意圖是發(fā)送已經(jīng)寫入的數(shù)據(jù)到DataOutputStream的服務(wù)器的緩沖區(qū)中。在某些電話上,這個操作工作正常,在其他的電話上,它導(dǎo)致HTTP請求的Transfer-Encoding被設(shè)置為"chunked",有一些隨機(jī)字符被放到請求本身的前面和后面。那又怎樣處理這個問題呢?這個方法調(diào)用實(shí)際上是根本不需要的。在接下來的一行中,服務(wù)器連接打開(通過openInputStream()),將自動輸入緩沖區(qū)。因此,你最好不要調(diào)用緩沖區(qū)的flush()方法。這個方法其余的部分保持不變,除了DataOutputStream對象必須在finally{;};語句塊中關(guān)閉。
};finally{;
if(hc!=null)hc.close();
if(dis!=null)dis.close();
if(dos!=null)dis.close();
};//結(jié)束try/finally
這就是所有的程序代碼!并請參見本文后附帶的程序代碼。
隨著可以使用國際互聯(lián)網(wǎng)絡(luò)和支持網(wǎng)絡(luò)的無線設(shè)備日益的增多普及,Java和J2ME的重要性也在不斷的變大。因?yàn)镠TTP協(xié)議是當(dāng)前僅有的,被所有的遵從MIDP規(guī)范的設(shè)備支持的網(wǎng)絡(luò)協(xié)議,它也是用于開發(fā)無線網(wǎng)絡(luò)應(yīng)用程序的最好的候選者。
在本文中,我們探究了無線網(wǎng)絡(luò)編程的基本結(jié)構(gòu)和幾個核心問題,我們看了如何調(diào)用兩個最常用的HTTP請求方法:GET和POST。J2ME仍然在它的發(fā)展初期,并且無線設(shè)備也即將得到大面積的普及。所以,所有有志投身于無線網(wǎng)絡(luò)編程中的開發(fā)者們將得到大展拳腳的好機(jī)會。
附錄:
/*
*HttpMidlet.java
*/
importjavax.microedition.midlet.*;
importjavax.microedition.lcdui.*;
importjavax.microedition.io.*;
importjava.io.*;
publicclassHttpMidletextendsMIDletimplementsCommandListener{;
//使用默認(rèn)的URL。用戶可以從圖形用戶接口改變這個值
//主MIDP顯示
privateDisplaymyDisplay=null;
//輸入URL的圖形用戶接口組件
privateformrequestScreen;
privateTextFieldrequestField;
//用于提交請求的圖形用戶接口組件
privateListlist;
privateString[]menuItems;
//用于顯示服務(wù)器響應(yīng)的圖形用戶接口組件
privateformresultScreen;
privateStringItemresultField;
//用于requestScreen的"send"按鈕
CommandsendCommand;
//用于requestScreen的"exit"按鈕
CommandexitCommand;
//用于requestScreen的"back"按鈕
CommandbackCommand;
publicHttpMidlet(){;
//初始化圖形用戶接口組件
myDisplay=Display.getDisplay(this);
sendCommand=newCommand("SEND",Command.OK,1);
exitCommand=newCommand("EXIT",Command.OK,1);
backCommand=newCommand("BACK",Command.OK,1);
//顯示請求的URL
requestScreen=newform("TypeinaURL:");
requestField=newTextField(null,defaultURL,100,TextField.URL);
requestScreen.append(requestField);
requestScreen.addCommand(sendCommand);
requestScreen.addCommand(exitCommand);
requestScreen.setCommandListener(this);
//選擇想要的HTTP請求方法
menuItems=newString[]{;"GETRequest","POSTRequest"};;
list=newList("SelectanHTTPmethod:",List.IMPLICIT,menuItems,null);
list.setCommandListener(this);
//先是從服務(wù)器上收到的信息
resultScreen=newform("ServerResponse:");
resultScreen.addCommand(backCommand);
resultScreen.setCommandListener(this);
};//結(jié)束HttpMidlet()
publicvoidstartApp(){;
myDisplay.setCurrent(requestScreen);
};//結(jié)束startApp()
publicvoidcommandAction(Commandcom,Displayabledisp){;
//當(dāng)用戶點(diǎn)擊"send"按鈕
if(com==sendCommand){;
myDisplay.setCurrent(list);
};elseif(com==backCommand){;
requestField.setString(defaultURL);
myDisplay.setCurrent(requestScreen);
};elseif(com==exitCommand){;
destroyApp(true);
notifyDestroyed();
};//結(jié)束if(com==sendCommand)
if(disp==list&&com==List.SELECT_COMMAND){;
Stringresult;
if(list.getSelectedIndex()==0)//發(fā)送一個GET請求到服務(wù)器
result=sendHttpGet(requestField.getString());
else//發(fā)送一個POST請求到服務(wù)器
result=sendHttpPost(requestField.getString());
resultField=newStringItem(null,result);
resultScreen.append(resultField);
myDisplay.setCurrent(resultScreen);
};//結(jié)束if(dis==list&&com==List.SELECT_COMMAND)
};//結(jié)束commandAction(Command,Displayable)
privateStringsendHttpGet(Stringurl)
{;
HttpConnectionhcon=null;
DataInputStreamdis=null;
StringBufferresponseMessage=newStringBuffer();
try{;
//使用READ權(quán)限的標(biāo)準(zhǔn)的HttpConnection
hcon=(HttpConnection)Connector.open(url);
//從HttpConnection取得一個DataInputStream
dis=newDataInputStream(hcon.openInputStream());
//從服務(wù)器上取回響應(yīng)
intch;
while((ch=dis.read())!=-1){;
responseMessage.append((char)ch);
};//結(jié)束while((ch=dis.read())!=-1)
};
catch(Exceptione)
{;
e.printStackTrace();
responseMessage.append("ERROR");
};finally{;
try{;
if(hcon!=null)hcon.close();
if(dis!=null)dis.close();
};catch(IOExceptionioe){;
ioe.printStackTrace();
};//結(jié)束try/catch
};//結(jié)束try/catch/finally
returnresponseMessage.toString();
};//結(jié)束sendHttpGet(String)
privateStringsendHttpPost(Stringurl)
{;
HttpConnectionhcon=null;
DataInputStreamdis=null;
DataOutputStreamdos=null;
StringBufferresponseMessage=newStringBuffer();
//請求體
Stringrequeststring="ThisisaPOST.";
try{;
//使用讀寫權(quán)限的HttpConnection
hcon=(HttpConnection)Connector.open(url,Connector.READ_WRITE);
//設(shè)置請求方法為POST
hcon.setRequestMethod(HttpConnection.POST);
//取得發(fā)送請求字符串的DataOutputStream
dos=hcon.openDataOutputStream();
byte[]request_body=requeststring.getBytes();
//發(fā)送請求字符串到服務(wù)器
for(inti=0;i<request_body.length;i++){;
dos.writeByte(request_body[i]);
};//結(jié)束for(inti=0;i<request_body.length;i++)
//取得做為接收服務(wù)器響應(yīng)的DataInputStream
dis=newDataInputStream(hcon.openInputStream());
//從服務(wù)器上取回響應(yīng)
intch;
while((ch=dis.read())!=-1){;
responseMessage.append((char)ch);
};//結(jié)束while((ch=dis.read())!=-1){;
};
catch(Exceptione)
{;
e.printStackTrace();
responseMessage.append("ERROR");
};
finally{;
//釋放輸入輸出流和HTTP連接
try{;
if(hcon!=null)hcon.close();
if(dis!=null)dis.close();
if(dos!=null)dos.close();
};catch(IOExceptionioe){;
ioe.printStackTrace();
};//結(jié)束try/catch
};//結(jié)束try/catch/finally
returnresponseMessage.toString();
};//結(jié)束sendHttpPost(String)
publicvoidpauseApp(){;
};//結(jié)束pauseApp()
publicvoiddestroyApp(booleanunconditional){;
myDisplay=null;
requestScreen=null;
requestField=null;
resultScreen=null;
resultField=null;
};//結(jié)束destroyApp(boolean)
};//結(jié)束HttpMidlet
新聞熱點(diǎn)
疑難解答
圖片精選