自己写代码 - 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放在客户端。
现在我的数据就是服务端的那一串字符串,我把数据拿过来,根据数据画界面,这,就是最淳朴的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放在客户端。
相关文章推荐
- 自己写代码 - HelloHi开发流水账 四 不止是String
- 自己写代码 - HelloHi开发流水账 五 一枚臭虫
- 自己写代码 - HelloHi开发流水账 一 先画个躯壳
- 自己写代码 - HelloHi开发流水账 二 先跑起来
- 自己写代码 HelloHi开发日记 六 掰开Ui和逻辑
- 反编译不完全的代码(自己动手写开发工具总结)
- iOS:使用Github托管自己本地的项目代码方式一:(Xcode方式:开发工具Xcode配置Git,由Xcode-->Source Control-->Commit)
- 在二次开发中怎么获取自己想要的那部分代码的位置
- 拉拉交友 http://www.les-sky.net 代码备份: 开发自己的可视化编辑器
- [c#]一步一步开发自己的自动代码生成工具之一:获取sql2005的数据库表结构
- [C#]一步一步开发自己的自动代码生成工具之四:简单三层代码模板实例Model层
- 使用百度地图SDK来开发自己的程序(主要是考察自己读代码的能力)
- Android 本机代码(Native Code)开发基础 -HelloJni代码详细解释
- 为了给自己开发一个支持 fastcgi 的 http server 做准备。剥离了 nanoweb 的 fastcgi 接口部分代码。测试了下。 成功了
- 如何在Eclipse中开发并调试自己的插件(或者说如何将自己的代码插件化)
- 微信.NET 微信开发 自己主动内容回复 ASP.NET C#代码
- 记录一下自己用STM32完整开发一台双模机械键盘的过程(附代码和原理图)
- 自己开发的一款基于PagedDataSource的datalist repeater控件,只需要在源程序中添加三行代码,即可实现通用分页
- 自己整理的一套java编码规则,让自己写代码不会那么乱
- 教你用最简单的JS代码,写出自己的模块化开发