您的位置:首页 > 编程语言

自己写代码 - HelloHi开发流水账 三 别再那么二

2010-02-02 11:39 232 查看
我用了一种最简单最二的update机制,不管3721的眉毛胡子一把抓的方法。update,从字面意思看这个函数就是根据当前的数据来修改界面以反映数据。
现在我的数据就是服务端的那一串字符串,我把数据拿过来,根据数据画界面,这,就是最淳朴的update。简单粗暴,但是不太有效率。因为对我来说,我的
数据不是那么的抽象,而是有一些约束的,比如,聊天的内容只会多不会少,说出来的话泼出去的水,也不能收回。这导致了原始的update勤勤恳恳做了很多
工作都是在浪费时间。每次只需要获取update新的消息就可以了,一来可以减少网络负担,二来,update后刷新整个contentPanel我认为
早晚是不会有好下场的。

这里还有一个问题值得考虑,自己发送的消息到底需不需要通过update的方式来显示呢,直接把它写到
contentPanel里面不更好吗?自己发送的信息先直接写到界面上,还真有这么干的。我就经常碰到,在msn或者qq上发出一条消息,过了一会儿又
收到一条消息称刚才的消息没发出去。能别这么玩人么?其实这就是异步调用的问题,在本例子中,如果在调用异步的sendMessage之前或者之后,把消
息写到contentPanel里面,到头来就会是那德行,如果把写contentPanel的代码放到异步方法类的onSuccess里面就没这个问题
了。这也是为什么把inputText清空的代码要写在这里。清空输入框在这里做似乎是没什么问题,那么显示发送成功的消息呢?应该还可能有问题,时间问
题。时间啊,这一切善恶的源头。发送成功一条消息,你可以确定的是这条消息会添加到服务端消息列表的末端,但是前一个消息是什么呢,不先更新一下,你是不
确定的。而即使我们在发送消息之前先更新,你也不能保证你发送之前没有人往里面加了更新的消息。这有点像版本控制,每次提交代码,你最好是先锁住,然后更
新,再提交,否则你更新和提交之间又可能产生新的版本。或者更大众的办法是提交到一个分支,然后merge到主干。我们的聊天系统要做成这样就太至于了。
如果本地没有版本呢?我们每次只是送一个字符串到服务端,然后update从服务端得到新的聊天内容,这样我根本不用在乎这句话是谁说的,谁说的都一样,
都是从服务端来的。至于update返回哪些字符串,我们可以在本地存放一个当前最新消息的编号,按这个编号去update新的消息。


在本地需要维持一个lastIndex记录上次读到哪条消息了,由于消息都按顺序存放,因此lastIndex和消息的数量正好是一样的。


HelloUi里面添加字段private
int lastIndex = 0;,
修改接口,把updateMessage加一个int lastIndex参数,实现改成这样

@Override

public String[] updateMessage(int clientId, int
lastIndex) {

String[] messageArray = new
String[messageList.size() - lastIndex];

return
messageList.subList(lastIndex,
messageList.size()).toArray(messageArray);

}
客户端那边updateMessage的onSuccess改为

@Override

public void onSuccess(String[]
result) {

for (String message : result) {

contentPanel.add(new
HTML(message));

lastIndex++;

}

scrollPanel.scrollToBottom();

}
Run一下,嗯,依然可以跑,而且比以前要顺畅一些了。修几个小bug

1.当我把滚动条拉上去看上面的
文字的时候,update就会把滚动条拉下来,这可不行。貌似有些聊天工具是这样做的,除非有新的消息,否则不会强制把滚动条拖到底。其实这也不是我喜欢
的方式,因为我要是把滚动条手动拉上去了,即使来了新消息,也许提醒我有新消息是善意的,但是把我找了半天的滚动条拉下去,我觉得不妥。我想改为,如果原
来滚动条是在底的,添加信息后还置为底,否则不动。大概看了下,以GWT提供的ScrollPanel似乎是做不到,也许通过DOM自己写
JavaScript可以做到,本着尽量简陋的原则,只好日后再说。先改成result没料的情况下直接返回。

@Override

public void onSuccess(String[]
result) {

if (result.length != 0) {

for (String message : result) {

contentPanel.add(new HTML(message));

lastIndex++;

}

scrollPanel.scrollToBottom();

}

}
凑合着先。

差不多也该实现一对一的聊天了,我大概的想法是,对于每一个client,在服务端都需要对
应一个对象,对象至少包含一个id,和一个room,room呢,就是聊天的环境,room里面至少要包含聊天内容,姑且保持一个String的
ArrayList。其实对于发消息,只需要知道room即可,但两个聊天的client最终肯定还是需要区别开来的不是吗,所以clientId还是必
要地,光是在客户端维持一个roomId还是不够。

添加HiClient类和HiRoom类,拖到现在至少getClientId必须要实现了。
后面的各种操作都要提供clientId,我需要根据clientId快速的找到相应的HiClient对象,还有什么比数组的随机寻址更简陋淳朴呢,先
这样做吧。创建一个类专门负责管理HiClient对象,定义好接口,这样即使后面改用其他数据结构,接口也不用改。接口至少应该提供
createClient,getClient,removeClient的功能,嗯,这就是一个manager类,取名
HiClientManager。

创建出一个新的HiClient对象还需要把它连接到一个有效地HiRoom对象。虽然将来还有很多问
题需要解决,比如room其实不仅仅是一个存放消息的容器,它还有状态,当room里面只有一个client,这还分几种情况,是刚创建的时候只有一个
client,还是经过一段对话后有一个client离开了。先不管那么多,不管room什么状态,连上了就往里写消息。

public
class HiClient {

private int id;

private HiRoom room;

public HiClient(int id) {

this.setId(id);

}

public void setId(int id) {

this.id = id;

}

public int getId() {

return id;

}

public
void setRoom(HiRoom room) {

this.room = room;

}

public HiRoom getRoom() {

return room;

}

}

public
class HiRoom {

private HiClient host;

private HiClient
guest;

private ArrayList<String> messageList = new
ArrayList<String>();

public HiRoom(HiClient host) {

this.host = host;

host.setRoom(this);

}

public void invite(HiClient guest) {

this.guest = guest;

guest.setRoom(this);

}

public void
setMessageList(ArrayList<String> messageList) {

this.messageList = messageList;

}

public
ArrayList<String> getMessageList() {

return
messageList;

}

}

public class HiClientManager {

private static final int CAPACITY = 222;

private HiClient[]
clientArray = new HiClient[CAPACITY];

private HiRoom
waitingRoom = null;

public HiClient createClient() {

for (int i = 0; i < CAPACITY; i++) {

if
(clientArray[i] == null) {

HiClient client = new
HiClient(i);

clientArray[i] = client;

if (waitingRoom != null) {

waitingRoom.invite(client);

waitingRoom = null;

} else {

waitingRoom = new
HiRoom(client);

}

return client;

}

}

return null;

}

public HiClient getClient(int id) {

return clientArray[id];

}

public void removeClient(int id) {

clientArray[id] = null;

}

}

@SuppressWarnings("serial")

public
class HelloServiceImpl extends RemoteServiceServlet

implements HelloService {

private HiClientManager clientMgr = new HiClientManager();

@Override

public int getClientId() {

return
clientMgr.createClient().getId();

}

@Override

public void sendMessage(int clientId, String message) {

ArrayList<String> messageList =
clientMgr.getClient(clientId).getRoom().getMessageList();

messageList.add(message);

}

@Override

public
String[] updateMessage(int clientId, int lastIndex) {

ArrayList<String> messageList =
clientMgr.getClient(clientId).getRoom().getMessageList();

String[] messageArray = new String[messageList.size() - lastIndex];

return messageList.subList(lastIndex,
messageList.size()).toArray(messageArray);

}

}


步接口的实现几乎没怎么改,只不过把原来全局的消息列表换成了根据clientId找到的room的消息列表。好了,现在聊天成了一对一的了。对应每个客
户端,服务端有一个HiClient对象,按理说任何接口只需要提供clientId,其他信息都可以在服务端得到,为何又需要提供lastIndex
呢。嗯,lastIndex可以存放在服务端,也可以保证每条信息只取一次,不会取重,但是万一网络发生异常,一次没取到的信息就再也取不到了,所以我还
是坚持把lastIndex放在客户端。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐