|
序列化方法serialize()很简单,仅仅是按照HTTP GET方法的格式进行查询条件组装,就像之前说到的,这里因为没有涉及到中文字符,所以无需进行UTF-8编码转换。”deserializes()”方法进行多对象的反序列化,并构造成TransAuto数组在手机上显示,需要强调的是根据分页策略,进行一次反序列化的TransAuto对象实例条数最多20条。服务器端返回的数据并不是按照HTTP GET方法的格式,而是根据表中字段的顺序再在中间加”&”进行分割的字符串,遗憾的是手机上String对象未提供”split()”方法,”Split.split(recString,"&")”方法化解了这一问题,下面是实现代码(来自于互联网)。
/**
* 分割字符串
*
* @param original 需要分割的字符串
* @paran regex 表达式
* @return 分割后生成的字符串数组
*/
public static String[] split(String original, String regex) {
int startIndex = 0;
Vector v = new Vector();
String[] str = null;
int index = 0;
startIndex = original.indexOf(regex);
while (startIndex < original.length() && startIndex != -1) {
String temp = original.substring(index, startIndex);
v.addElement(temp);
index = startIndex + regex.length();
startIndex = original.indexOf(regex, startIndex + regex.length());
}
v.addElement(original.substring(index + 1 - regex.length()));
str = new String[v.size()];
for (int i = 0; i < v.size(); i++) {
str[i] = (String) v.elementAt(i);
}
return str;
}
一旦所有前期工作都准备好,将是最关键的GoodsFindAutoThread线程类连接服务器返回数据,为了让界面美观,在进入线程类之后GoodsFindAuto类马上通过”Navigator.display.setCurrent(WaitForm.getInstance())”代码把画面转移到WaitForm,一个等待画面,上面写着“正在连接服务器,请稍等...”,直到线程类和服务器交互完毕,这个画面将由线程类负责切换。
package com.forbidden.thread;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.rms.RecordStore;
import com.forbidden.screen.*;
import com.forbidden.util.Split;
import com.forbidden.vo.TransAuto;
/* 货主找车线程
* @author rosen jiang
* @since 2005-12
*/
public class GoodsFindAutoThread extends Thread{
private String url; //服务器地址
private String autoFrom; //车辆出发地
private String autoTarget; //车辆目的地
private String pubDate; //发布日期
/**
* 构造函数
*
* @param page 请求页数
* @param perPage 每页条数
* @param ta 构造好的条件对象
*/
public GoodsFindAutoThread(int page,int perPage,TransAuto ta){
String userName=null;
try {
//取出当前用户名,以便进行数据库端日志记录。
RecordStore rs = RecordStore.openRecordStore("app",false);
userName = new String(rs.getRecord(1));
rs.closeRecordStore();
} catch (Exception e) {
}
this.url = "http://127.0.0.1:8080/AirTransport/servlet/GoodsFindAuto?userName="
+ userName+"&page="+page+"&perPage="+perPage+"&"+ta.serialize();
this.autoFrom = ta.getAutoFrom();
this.autoTarget = ta.getAutoTarget();
this.pubDate = ta.getPubDate();
}
public void run(){
InputStream is = null;
HttpConnection c = null;
try {
c = (HttpConnection)Connector.open(url);
//接收输出流
is = new DataInputStream(c.openInputStream());
int i = (int) c.getLength();
byte [] recData = new byte[i];
is.read(recData);
is.close();
c.close();
//未找到符合条件的车辆
if (recData[0]+recData[1]!=0) {
ByteArrayInputStream bin = new ByteArrayInputStream(recData);
DataInputStream din = new DataInputStream(bin);
Alert al = new Alert ("查询结果",din.readUTF(),null,AlertType.CONFIRMATION);
al.setTimeout(Alert.FOREVER);
din.close();
bin.close();
Navigator.display.setCurrent(al,GoodsFindAuto.getInstance());
} else {
//找到符合条件的车辆
//丢掉两个错误信息字节数据
byte[] reRecData = new byte[recData.length-2];
System.arraycopy(recData,2,reRecData,0,recData.length-2);
ByteArrayInputStream bin = new ByteArrayInputStream(reRecData);
//结果数据输入流
DataInputStream dis = new DataInputStream(bin);
int count = Integer.parseInt(dis.readUTF()); //总数
int total = Integer.parseInt(dis.readUTF()); //总页数
int current = Integer.parseInt(dis.readUTF()); //当前页码
TransAuto ta = new TransAuto(null, null, null, null, null, null, null, null);
TransAuto[] tas = null;
tas = ta.deserializes(autoFrom,20,dis);
dis.close();
bin.close();
Navigator.display.setCurrent(GFAList.getInstance(count,total,current,autoFrom,autoTarget,pubDate,tas));
}
} catch(Exception e){
Alert al = new Alert ("查询发生错误",e.toString(),null,AlertType.ERROR);
al.setTimeout(Alert.FOREVER);
Navigator.display.setCurrent(al,GoodsFindAuto.getInstance());
}
}
}
在GoodsFindAutoThread()构造函数中,首先取出了记录在RMS中的当前登陆者名,以便服务器记录用户的查询条件,以后为不同的客户提供个性化服务,接着把用户输入的检索条件记录下来,取第5页的时候要用到,然后按照之前构造好的TransAuto实例进行序列化生成URL字符串。在”run()”方法中,为了判断是否返回了正确数据,我在服务器端的代码中设置了标识,既:如果查询结果为0条或服务器端发生任何错误,那么返回数据的前个两字节不为空,反之则亦然。收到正确的查询结果之后,先丢弃前两个字节,然后转换成输入流,”int count = Integer.parseInt(dis.readUTF())”第一组字节流是总记录数,”int total = Integer.parseInt(dis.readUTF())”第二组字节流是总页数,”int current = Integer.parseInt(dis.readUTF())”第三组字节流是当前页码。取完基本信息后,接着取车辆数据,通过”tas = ta.deserializes(autoFrom,20,dis)”反序列化之后得到TransAuto数组对象。最后进入GFAList类进行数据列表,代码” Navigator.display.setCurrent(GFAList.getInstance(count,total,current,autoFrom,autoTarget,pubDate,tas))”把检索条件全部记录下来,其做用也是为了再次分页的需要。
package com.forbidden.screen;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.List;
import com.forbidden.thread.GoodsFindAutoThread;
import com.forbidden.vo.TransAuto;
/* 货主找车列表
* @author rosen jiang
* @since 2005-12
*/
public class GFAList extends List implements CommandListener{
private Command backCommand;
private Command funCommand;
private Command preCommand;
private Command nextCommand;
private static String autoFrom; //车辆出发地
private static String autoTarget; //车辆目的地
private static String pubDate; //发布日期
private static int count; //总数
private static int total; //总页数
private int current; //当前页码
private TransAuto[] newTas; //车辆数组
public static Displayable instance; //对象实例
/**
* 获取对象实例
*
* @param coun //总数
* @param tot //总页数
* @param curr //当前页码
* @param from //车辆出发地
* @param target //车辆目的地
* @param date //发布日期
* @param tas //车辆数组
* @return
*/
synchronized public static Displayable getInstance(int coun,int tot,int curr,String from,String target,String date,TransAuto[] tas){
autoFrom = from;
count = coun;
autoTarget = target;
pubDate = date;
total = tot;
instance = new GFAList(curr,tas,List.IMPLICIT);
return instance;
}
/**
* 构造函数
* @param curr //当前页码
* @param tas //车辆数组
* @param i //表现方式
*/
public GFAList(int curr,TransAuto[] tas,int i){
super(curr+"/"+total+"页 共"+count+"条记录",i);
this.current = curr;
this.newTas = tas;
for(int j=0; j<5; j++){
try{
TransAuto ta = null;
if(curr%4==0){
ta = tas[j+15];
}else if(curr%4==1){
ta = tas[j];
}else if(curr%4==2){
ta = tas[j+5];
}else if(curr%4==3){
ta = tas[j+10];
}
append(ta.getName()+","
+ta.getPhone()+".",null);
}catch(java.lang.NullPointerException e){
break;
}
}
backCommand = new Command("货主找车", Command.BACK, 1);
funCommand = new Command("详情", Command.SCREEN, 1);
preCommand = new Command("上一页", Command.SCREEN, 1);
nextCommand = new Command("下一页", Command.SCREEN, 1);
addCommand(backCommand);
addCommand(funCommand);
addCommand(preCommand);
addCommand(nextCommand);
setCommandListener(this);
}
/**
* 对用户输入命令作出反应
* @param c 命令
* @param s Displayable 对象
*/
public void commandAction(Command c, Displayable s) {
String cmd = c.getLabel();
if (cmd.equals("货主找车")){
Navigator.flow(cmd);
//翻页(下一页)处理
} else if (cmd.equals("下一页")){
if(total == current){
Alert al = new Alert ("提示","已到达最后一页。",null,AlertType.CONFIRMATION);
al.setTimeout(Alert.FOREVER);
Navigator.display.setCurrent(al);
} else if (current % 4 != 0){
Navigator.display.setCurrent(getInstance(count,total,current+1,autoFrom,autoTarget,pubDate,newTas));
//如果当前页已经是4的倍数页,那么连接服务器取下20条数据。
} else if (current % 4 == 0){
TransAuto ta = new TransAuto(null,null,null,null,
pubDate,autoFrom,autoTarget, null);
GoodsFindAutoThread gfat = new GoodsFindAutoThread(current+1,20,ta);
Navigator.display.setCurrent(WaitForm.getInstance());
gfat.start();
}
//翻页(上一页)处理
} else if (cmd.equals("上一页")){
if(current == 1){
Alert al = new Alert ("提示","这是第一页。",null,AlertType.CONFIRMATION);
al.setTimeout(Alert.FOREVER);
Navigator.display.setCurrent(al);
} else if ((current - 1) % 4 != 0){
Navigator.display.setCurrent(getInstance(count,total,current-1,autoFrom,autoTarget,pubDate,newTas));
//如果当前页已经是4的倍数页,那么连接服务器取上20条数据。
} else if ((current - 1) % 4 == 0){
TransAuto ta = new TransAuto(null,null,null,null,
pubDate,autoFrom,autoTarget, null);
GoodsFindAutoThread gfat = new GoodsFindAutoThread(current-1,20,ta);
Navigator.display.setCurrent(WaitForm.getInstance());
gfat.start();
}
//详情处理
} else if (cmd.equals("详情")){
if(current % 4 == 0){
Navigator.display.setCurrent(GFAInfo.getInstance(newTas[getSelectedIndex()+15]));
} else if (current % 4 == 1){
Navigator.display.setCurrent(GFAInfo.getInstance(newTas[getSelectedIndex()]));
} else if (current % 4 == 2){
Navigator.display.setCurrent(GFAInfo.getInstance(newTas[getSelectedIndex()+5]));
} else if (current % 4 == 3){
Navigator.display.setCurrent(GFAInfo.getInstance(newTas[getSelectedIndex()+10]));
}
}
}
}
构造函数GFAList除了创建几个命令以外,最主要的工作是显示本页数据,通过当前页码与4取模之后再从TransAuto数组得到准确数据并显示在屏幕上。还是让我们先睹为快吧,这次截图不是模拟器上,而是运行在我的索爱K700c上真实环境。
看完截图,回到”commandAction()”方法上,当用户进行”下一页”操作的时候,首先判定当前页是不是最后一页,如果是”Alert al = new Alert ("提示","已到达最后一页。",null,AlertType.CONFIRMATION)”代码给用户一个Alert提示。如果当前页是4的倍数页,”GoodsFindAutoThread gfat = new GoodsFindAutoThread(current+1,20,ta)”代码连接服务器取回下20条数据,这个时候要进入WaitForm画面进行等待,就像首次进行查询一样,之前一直保存着的查询参数也就派上用场了。如果还不是4的倍数,”Navigator.display.setCurrent(getInstance(count,total,current+1,autoFrom,autoTarget,pubDate,newTas))”这行代码就把修改后的数据再次送到GFAList对象的构造函数中,重新实例化画面,也就在手机上实现了不用连接服务器的快速翻页。”上一页”操作和刚才谈到的”下一页”操作类似,就不多说了。如果用户点击”详情”看某条车辆信息的时候,需要计算被点击信息所处的绝对位置,比如当前页码正好是4的倍数,那么可以断定该条信息处于整个TransAuto数组的末端,也就是最后一页,然后取得这条记录的相对位置(例如3)再加上15,就是这条记录的绝对位置了,其他位置以此类推,”Navigator.display.setCurrent(GFAInfo.getInstance(newTas[getSelectedIndex()+15]))”代码把某个选中的车辆信息送入GFAInfo对象以显示详细信息。
package com.forbidden.screen;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.TextField;
import com.forbidden.vo.TransAuto;
/* 车辆详情
* @author rosen jiang
* @since 2005-12
*/
public class GFAInfo extends Form implements CommandListener{
private Command backCommand;
private static Displayable instance;
private TextField nameField;
private TextField autoNoFiled;
private TextField phoneFiled;
private TextField autoCapField;
private TextField pubDateField;
private TextField autoFromField;
private TextField autoTargetField;
private TextField memoField;
/**
* 获取对象实例
* @param ta 车辆对象
* @return
*/
synchronized public static Displayable getInstance(TransAuto ta){
instance = new GFAInfo(ta);
return instance;
}
/**
* 构造函数
*
* @param ta 车辆对象
*/
public GFAInfo(TransAuto ta){
super("车辆详情");
backCommand = new Command("返回", Command.BACK, 1);
nameField = new TextField("车主", ta.getName(), 25, TextField.ANY);
autoNoFiled = new TextField("车牌号", ta.getAutoNo(), 25, TextField.ANY);
phoneFiled = new TextField("电话", ta.getPhone(), 25, TextField.ANY);
autoCapField = new TextField("重量", ta.getAutoCap(), 25, TextField.ANY);
pubDateField = new TextField("发布日期", ta.getPubDate(), 25, TextField.ANY);
autoFromField = new TextField("起始地", ta.getAutoFrom(), 25, TextField.ANY);
autoTargetField = new TextField("目的地", ta.getAutoTarget(), 25, TextField.ANY);
memoField = new TextField("备注", ta.getMemo(), 300, TextField.ANY);
append(nameField);
append(autoNoFiled);
append(phoneFiled);
append(autoCapField);
append(pubDateField);
append(autoFromField);
append(autoTargetField);
append(memoField);
addCommand(backCommand);
setCommandListener(this);
}
/**
* 对用户输入命令作出反应
* @param c 命令
* @param s Displayable 对象
*/
public void commandAction(Command c, Displayable s) {
Navigator.display.setCurrent(GFAList.instance);
}
}
GFAInfo 类代码显得很简单了,”getInstance()”方法每次都根据数据的不同重新构造实例,”GFAInfo()”构造函数则把TransAuto对象属性一一呈现出来,手机端实现也就告一段落了。
|