从Java EE到Java ME的通讯(2)

2007-10-09     浏览:1329     来源:SOFT6
关键词:

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