|
序列化方法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对象属性一一呈现出来,手机端实现也就告一段落了。 |