|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
什么时候上述的三种开发工具能和三为一,什么时候java的竞争力才更强,才有机会拉拢更多的程序员投入到对java的开发上,因为到时的开发工具将会比.net的更简单。还有一点也很关键,什么时候java推出的jsf能成为真正意义上的标准。编程 因为无线设备所能撑持的收集协定十分无限,仅限于HTTP,Socket,UDP等几种协定,分歧的厂家大概还撑持其他收集协定,可是,MIDP1.0标准划定,HTTP协定是必需完成的协定,而其他协定的完成都是可选的。因而,为了能在分歧范例的手机上移植,我们只管接纳HTTP作为收集毗连的首选协定,如许还能重用服务器真个代码。可是,因为HTTP是一个基于文本的效力较低的协定,因而,必需细心思索手机和服务器真个通讯内容,尽量地进步效力。
关于MIDP使用程序,应该只管做到:
1.发送哀求时,附加一个User-Agent头,传进MIDP和本身版本号,以燕服务器能辨认此哀求来自MIDP使用程序,而且依据版本号发送响应的响应。
2.毗连服务器时,显现一个下载进度条利用户能看到下载进度,并能随时中止毗连。
3.因为无线收集毗连速率还很慢,因而有需要将某些数据缓存起来,能够存储在内存中,也能够放到RMS中。
关于服务器端而言,其输入呼应应该只管做到:
1.明白设置Content-Length字段,以便MIDP使用程序能读取HTTP头并判别本身是不是有才能处置此长度的数据,假如不克不及,能够间接封闭毗连而不用持续读取HTTP注释。
2.服务器不该当发送HTML内容,由于MIDP使用程序很难明析HTML,XML固然可以剖析,可是泯灭CPU和内存资本,因而,应该发送松散的二进制内容,用DataOutputStream间接写进并设置Content-Type为application/octet-stream。
3.只管不要重定向URL,如许会招致MIDP使用程序再次毗连服务器,增添了用户的守候工夫和收集流量。
4.假如产生非常,比方哀求的资本未找到,大概身份考证失利,一般,服务器会向扫瞄器发送一个显现堕落的页面,大概还包含一个用户登录的Form,可是,向MIDP发送毛病页面毫偶然义,应该间接发送一个404或401毛病,如许MIDP使用程序就能够间接读取HTTP头的呼应码猎取毛病信息而不用持续读取响应内容。
5.因为服务器的盘算才能远远凌驾手机客户端,因而,针对分歧客户端版本发送分歧呼应的义务应当在服务器端完成。比方,依据客户端传送的User-Agent头断定客户端版本。如许,低版本的客户端不用晋级也能持续利用。
MIDP的联网框架界说了多种协定的收集毗连,可是每一个厂商都必需完成HTTP毗连,在MIDP2.0中还增添了必需完成的HTTPS毗连。因而,要包管MIDP使用程序能在分歧厂商的手机平台上移植,最好只利用HTTP毗连。固然HTTP是一个基于文本的效力较低的协定,可是因为利用出格普遍,年夜多半服务器使用的前端都是基于HTTP的Web页面,因而能最年夜限制地复用服务器真个代码。只需把持好缓存,仍旧有不错的速率。
SUN的MIDP库供应了javax.microediton.io包,能十分简单地完成HTTP毗连。可是要注重,因为收集有很年夜的延时,必需把联网操纵放进一个独自的线程中,以免主线程堵塞招致用户界面中断呼应。现实上,MIDP运转情况基本就不同意在主线程中操纵收集毗连。因而,我们必需完成一个天真的HTTP联网模块,能让用户十分直不雅地看到以后上传和下载的进度,而且可以随时作废毗连。
一个完全的HTTP毗连为:用户经由过程某个命令倡议毗连哀求,然后体系给出一个守候屏幕提醒正在毗连,当毗连一般停止后,行进到下一个屏幕并处置下载的数据。假如毗连历程呈现非常,将给用户提醒并前往到前一个屏幕。用户在守候过程当中可以随时作废并前往前一个屏幕。
我们计划一个HttpThread线程类卖力在背景毗连服务器,HttpListener接话柄现Observer(察看者)形式,以便HttpThread能提醒察看者下载入手下手、下载停止、更新进度条等。HttpListener接口以下:
publicinterfaceHttpListener{
voidonSetSize(intsize);
voidonFinish(byte[]data,intsize);
voidonProgress(intpercent);
voidonError(intcode,Stringmessage);
}
完成HttpListener接口的是承继自Form的一个HttpWaitUI屏幕,它显现一个进度条和一些提醒信息,并同意用户随时中止毗连:
publicclassHttpWaitUIextendsFormimplementsCommandListener,HttpListener{
privateGaugegauge;
privateCommandcancel;
privateHttpThreaddownloader;
privateDisplayabledisplayable;
publicHttpWaitUI(Stringurl,Displayabledisplayable){
super("Connecting");
this.gauge=newGauge("Progress",false,100,0);
this.cancel=newCommand("Cancel",Command.CANCEL,0);
append(gauge);
addCommand(cancel);
setCommandListener(this);
downloader=newHttpThread(url,this);
downloader.start();
}
publicvoidcommandAction(Commandc,Displayabled){
if(c==cancel){
downloader.cancel();
ControllerMIDlet.goBack();
}
}
publicvoidonFinish(byte[]buffer,intsize){…}
publicvoidonError(intcode,Stringmessage){…}
publicvoidonProgress(intpercent){…}
publicvoidonSetSize(intsize){…}
}
HttpThread是卖力处置Http毗连的线程类,它承受一个URL和HttpListener:
classHttpThreadextendsThread{
privatestaticfinalintMAX_LENGTH=20*1024;//20K
privatebooleancancel=false;
privateStringurl;
privatebyte[]buffer=null;
privateHttpListenerlistener;
publicHttpThread(Stringurl,HttpListenerlistener){
this.url=url;
this.listener=listener;
}
publicvoidcancel(){cancel=true;}
}
利用GET猎取内容
我们先会商最复杂的GET哀求。GET哀求只需向服务器发送一个URL,然后获得服务器呼应便可。在HttpThread的run()办法中完成以下:
publicvoidrun(){
HttpConnectionhc=null;
InputStreaminput=null;
try{
hc=(HttpConnection)Connector.open(url);
hc.setRequestMethod(HttpConnection.GET);//默许即为GET
hc.setRequestProperty("User-Agent",USER_AGENT);
//getresponsecode:
intcode=hc.getResponseCode();
if(code!=HttpConnection.HTTP_OK){
listener.onError(code,hc.getResponseMessage());
return;
}
//getsize:
intsize=(int)hc.getLength();//前往呼应巨细,大概-1假如巨细没法断定
listener.onSetSize(size);
//入手下手读呼应:
input=hc.openInputStream();
intpercent=0;//percentage
inttmp_percent=0;
intindex=0;//bufferindex
intreads;//eachbyte
if(size!=(-1))
buffer=newbyte[size];//呼应巨细已知,断定缓冲区巨细
else
buffer=newbyte[MAX_LENGTH];//呼应巨细未知,设定一个流动巨细的缓冲区
while(!cancel){
intlen=buffer.length-index;
len=len>128?128:len;
reads=input.read(buffer,index,len);
if(reads<=0)
break;
index+=reads;
if(size>0){//更新进度
tmp_percent=index*100/size;
if(tmp_percent!=percent){
percent=tmp_percent;
listener.onProgress(percent);
}
}
}
if(!cancel&&input.available()>0)//缓冲区已满,没法持续读取
listener.onError(601,"Bufferoverflow.");
if(!cancel){
if(size!=(-1)&&index!=size)
listener.onError(102,"Content-Lengthdoesnotmatch.");
else
listener.onFinish(buffer,index);
}
}
catch(IOExceptionioe){
listener.onError(101,"IOException:"+ioe.getMessage());
}
finally{//清算资本
if(input!=null)
try{input.close();}catch(IOExceptionioe){}
if(hc!=null)
try{hc.close();}catch(IOExceptionioe){}
}
}
当下载终了后,HttpWaitUI就取得了来自服务器的数据,要传送给下一个屏幕处置,HttpWaitUI必需包括对此屏幕的援用并经由过程一个setData(DataInputStreaminput)办法让下一个屏幕能十分便利地读取数据。因而,界说一个DataHandler接口:
publicinterfaceDataHandler{
voidsetData(DataInputStreaminput)throwsIOException;
}
HttpWaitUI呼应HttpThread的onFinish事务并挪用下一个屏幕的setData办法将数据传送给它并显现下一个屏幕:
publicvoidonFinish(byte[]buffer,intsize){
byte[]data=buffer;
if(size!=buffer.length){
data=newbyte[size];
System.arraycopy(data,0,buffer,0,size);
}
DataInputStreaminput=null;
try{
input=newDataInputStream(newByteArrayInputStream(data));
if(displayableinstanceofDataHandler)
((DataHandler)displayable).setData(input);
else
System.err.println("[WARNING]Displayableobjectcannothandledata.");
ControllerMIDlet.replace(displayable);
}
catch(IOExceptionioe){…}
}
以下载一则旧事为例,一个完全的HTTPGET哀求历程以下:
起首,用户经由过程点击某个屏幕的命令但愿浏览指定的一则旧事,在commandAction事务中,我们初始化HttpWaitUI和显现数据的NewsUI屏幕:
publicvoidcommandAction(Commandc,Displayabled){
HttpWaitUIwait=newHttpWaitUI("http://192.168.0.1/news.do?id=1",newNewsUI());
ControllerMIDlet.forward(wait);
}
NewsUI完成DataHandler接口并卖力显现下载的数据:
publicclassNewsUIextendsFormimplementsDataHandler{
publicvoidsetData(DataInputStreaminput)throwsIOException{
Stringtitle=input.readUTF();
Datedate=newDate(input.readLong());
Stringtext=input.readUTF();
append(newStringItem("Title",title));
append(newStringItem("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哀求:
POSThttp://127.0.0.1/login.doHTTP/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;MSIE6.0;WindowsNT5.1)
Content-Length:28
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将注释写进:
OutputStreamoutput=hc.openOutputStream();
output.write(data);
必要注重的是,数据仍必要以URL编码格局编码,因为MIDP库中没有J2SE中与之对应的URLEncoder类,因而,必要本人下手编写这个encode()办法,能够参考java.net.URLEncoder.java的源码。剩下的即是读取服务器呼应,代码与GET分歧,这里就不再胪陈。
利用multipart/form-data发送文件
假如要在MIDP客户端向服务器上传文件,我们就必需摹拟一个POSTmultipart/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"
Today
--ABCD
Content-Disposition:form-data;name="1.txt";filename="C:1.txt"
Content-Type:text/plain
<这里是1.txt文件的内容>
--ABCD--
请注重,每行都必需以
停止,包含最初一行。假如用Sniffer程序检测IE发送的POST哀求,能够发明IE的分开符相似于---------------------------7d4a6d158c9,这是IE发生的一个随机数,目标是避免上传文件中呈现分开符招致服务器没法准确辨认文件肇端地位。我们能够写一个流动的分开符,只需充足庞大便可。
发送文件的POST代码以下:
<Pclass=code>String[]props=...//字段名
String[]values=...//字段值
byte[]file=...//文件内容
StringBOUNDARY="---------------------------7d4a6d158c9";//分开符
StringBuffersb=newStringBuffer();
//发送每一个字段:
for(inti=0;i |
|