自己写代码 - HelloHi开发流水账 二 先跑起来

来源:互联网 发布:雪梨的淘宝店是哪个店 编辑:程序博客网 时间:2024/05/16 13:01

刚开始一切随简,先让人能够说话。
Web Application不同于传统的网站在于客户端和程序的交互,然而在这里,随机聊天系统里不再是客户端和服务器的交互,而是客户端和客户端之间交互,服务器为这种交互提供了中转。因此我要做的就是客户端C1发送消息到服务器,然后另外的客户端C2在服务器上取得消息,就可以实现聊天。唯一不爽的是,C1对C2说“Hi”,C2什么时候能听见不取决于C1什么时候说,而是取决于C2什么时候想听,因此这不是实时的。用一个Timer不停的更新可以做出一种貌似实时的样子。

修改RPC接口,看看需要些什么。
客户端要和客户端交互,因此必须要把他们区别开来,每个客户端必须有一个id。因此连上系统第一步需要去取得一个有效的id。之后的通信,服务端都需要区分客户端,因此都要提供客户端id。发送消息只需要再提供一个String表示消息内容就可以了。更新消息的策略值得思考。本着先跑起来的思想,先搞个简单的,每次把所有的消息都拿过来好了,给一个clientId,返回一个String数组。
修改GreetingService,删除原来的String greetServer(String name);添加新的方法。

@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
    int getClientId();
    void sendMessage(int clientId, String message);
    String[] updateMessage(int clientId);
}

修改相应的异步接口和实现。
现在这个Service已经不提供Greeting方法,它的名字让我很不爽,Refactor之。改成HelloService。打开web.xml,可以看出service是怎么映射起来的:

<servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>com.gmail.hy87cn.server.HelloServiceImpl</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/hellohi/greet</url-pattern>
  </servlet-mapping>

我觉得这个路径名和servlet名字也都不合适,改成hello,RemoteServiceRelativePath标注改成“hello”,然后把web.xml里面的也改掉。

在HelloUi里面添加两个字段

    private int id = -1;
    private HelloServiceAsync service = GWT.create(HelloService.class);

再添加getClient方法

    private void initClientId() {
        service.getClientId(new AsyncCallback<Integer>() {

            @Override
            public void onFailure(Throwable caught) {
                // TODO Auto-generated method stub
                
            }

            @Override
            public void onSuccess(Integer result) {
                id = result;
            }
            
        });
    }

远程调用都是异步方法,并且你是不能肯定调用一定成功的,除了保证代码正确,远程调用是否成功还取决于网络状况,服务器抽风程度等。因此我们不能够get一次clientId就不管了,而是不停的get直到get到有效地id为止。什么时候做这个动作呢,如果想不清楚,姑且就delay到需要clientId的时候。

再添加一个Timer和update方法。

    private Timer timer = new Timer() {

        @Override
        public void run() {
            update();
        }
        
    };

 

    private void update() {
        if (clientId == -1) {
            initClientId();
        } else {
            service.updateMessage(clientId, new AsyncCallback<String[]>() {

                @Override
                public void onFailure(Throwable caught) {
                    // TODO Auto-generated method stub
                    
                }

                @Override
                public void onSuccess(String[] result) {
                    contentPanel.clear();
                    for (String message : result) {                    
                        contentPanel.add(new HTML(message));
                    }
                }
            
            });
        }

    }

由于是异步调用,我们不能在initClientId调用后马上调用updateMessage,因为initClientId之后clientId也还不一定就就绪了。
然后在构造函数里加上timer.scheduleRepeating(2000);//ms这句,timer就会开始以2秒为间隔执行run函数,run会调用update。

我总是喜欢刚开始的时候尽量简单,所以连发送消息的按钮都省了,用回车键吧。在构造函数里给
inputText添加一个KeyPressHandler,响应按键事件,判断如果是按回车键,就调用sendMessage方法。

        inputText.addKeyPressHandler(new KeyPressHandler() {

            @Override
            public void onKeyPress(KeyPressEvent event) {
                if (event.getCharCode() == KeyCodes.KEY_ENTER) {
                    sendMessage();
                }
            }
            
        });

 

    private void sendMessage() {
        String input = inputText.getText();
        if (!input.isEmpty()) {
            service.sendMessage(clientId, input, new AsyncCallback<Void>() {

                @Override
                public void onFailure(Throwable caught) {
                    // TODO Auto-generated method stub
                    
                }

                @Override
                public void onSuccess(Void result) {
                    inputText.setText("");
                }
                
            });
        }
    }

如果发消息成功就把inputText清空。

客户端大抵就是这个样子了,很简陋,不过没关系,第一个版本的服务端将会更加简陋。
随机的聊天系统是一对一的,可以想象成两个客户端会进入同一个聊天室,发送信息不是发送给对方,而是发送给聊天室。这里就有两个问题,一是说话的问题,一是分配聊天室的问题。福尔摩斯说一次只考虑一个问题,因此现在我只考虑说话的问题,先搞成所有客户端公用一个聊天室。够简陋了吧。呃,这样一来其实连clientId都可以省了。getClientId直接返回一个0吧,只要不是-1就行。然后sendMessage把这些String存放到一个ArrayList,updateMessages把这些String传回来。
在HelloServiceImpl里面添加private ArrayList<String> messageList = new ArrayList<String>();
然后实现sendMessage方法和updateMessages方法。

    @Override
    public void sendMessage(int clientId, String message) {
        messageList.add(message);
    }

    @Override
    public String[] updateMessage(int clientId) {
        String[] messageArray = new String[messageList.size()];
        return messageList.toArray(messageArray);
    }

Run,可以了,开三个网页,说话互相都能看见。虽然消息是谁说的都不知道,陋是陋了点,第一步先这样吧。先更正一些小问题。
1.页面打开后要等2秒钟才会执行第一次update,我希望他一开始先做一次。
2.发送消息后,要等下一次update才会看到自己发出的消息,我希望发送消息之后先update一下。
3.在contentPanel里面添加东西之后把滚动条拖到最后。
在构造函数的timer.scheduleRepeating(2000);//ms之前调用一下timer.schedule(1);,在sendMessage的onSuccess里面调用update,在添加新内容到contentPanel之后,调用scrollPanel.scrollToBottom();
看上去好一点了,不过还是很挫,跑是能跑了,接下来就来解决挫的问题。

原创粉丝点击