作者PsMonkey (痞子军团团长)
看板java
标题GWT 与 WebSocket [上]
时间Fri Jul 5 18:03:22 2013
Blog 版:
http://blog.dontcareabout.us/2013/07/gwt-websocket.html
BBS 版以 markdown 格式撰写
______________________________________________________________________
前言
====
这一阵子因为专案需要 server 与 browser 之间有即时双向沟通的能力,
所以就用了 websocket。
又由於 server 端绑定 PHP(还好 browser 也绑定 Chrome XD),
再加上传输的资料<strike>还</strike>没有很复杂,单纯字串就可以解决,
所以没有用现有的 GWT websocket library,统统自己来了...... [遮脸]
於是也就顺便写了这篇文章,借 websocket 介绍下面两个主题:
1. GWT 如何整合 JavaScript 程式码(JSNI)
1. EventHandler 的使用
(当然行有余力的话也想涵盖 GWT RPC,不过目前无法)
这三个主题在作〈[GWT 版 GAE Channel API]〉的时候都有用到
(但是 RPC 的部份只抄其 code、不明其理 [遮脸]),
不过拿 websocket 来介绍可能比较实在一点,
GAE 的 channel API 可能太少人用了 [死]。
[GWT 版 GAE Channel API]:
http://blog.dontcareabout.us/2012/04/gwt-gae-channel-api.html
webocket
========
因为我们要用 GWT 把 websocket 给包起来,
所以还是得理解一下 websocket client 端的行为。
// ======== JavaScript code ======== //
//建立 websocket,目前似乎 FOO_PATH 打啥都没差,
//主要还是看 address 跟 port 号
var websocket = new WebSocket("ws://localhost:8000/FOO_PATH");
websocket.onopen = function(evnt) { alert("connected!"); }
websocket.onmessage = function(evnt) {
alert("Receive : " + evnt.data);
}
//其他还有 websocket.onerror 跟 websocket.onclose,自己比照办理
//送资料
websocket.send(SOMETHING);
大抵上可以望文生意,就不特别作解释了 [逃]
JSNI
====
JSNI 全名 JavaScript Native Interface,
用不太严谨的类比讲法就是 GWT 中的 JNI,不过没有 JNI 那麽复杂。
毕竟从 GWT compile 完的最终结果来看,所有程式码统统会变成 JavaScript,
没有 JavaScript 反向呼叫 Java code 的问题,因此单纯很多。
开发人员可以透过 JSNI 把某一些 JavaScript 程式码重新包装成 Java code,
然後就可以完全用 Java 的思维与写法去使用它(其实 GWT 底层满满的都是 JSNI)。
例如 JavaScript 当中常用的 `console.log()`,就可以这样包起来:
// ======== Java code ======== //
public class Console {
public static native void log(String message) /*-{
$wnd.console.log(message);
}-*/;
}
//使用
Console.log("hello world");
完整的 JSNI 文件可以参阅 [GWT 文件],
或是这两篇中文 Blog:[DontCare 1]、[DontCare 2]。
这篇文章只针对必要的部份作讲解。
(其实也没什麽好讲的 XD)
[GWT 文件]:
http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
[DontCare 1]:
http://blog.dontcareabout.us/2010/02/gwt-part-1jsni_8514.html
[DontCare 2]:
http://blog.dontcareabout.us/2010/02/
gwt-part-2javascript-overlay-type_9960.html
要把 JavaScript 的 websocket 包起来,会稍微复杂一点。
首先必须得要建立一个 `JsWebSocket` 物件像这样:
// ======== Java code ======== //
class JsWebSocket extends JavaScriptObject {
//必须要有一个 protected 的 contructor
protected JsWebSocket() {}
//将 WebSocket.send() 包成 Java method
public native final void send(String msg) /*-{
this.send(msg);
}-*/;
}
`JsWebSocket` 算是 Java object 与 JavaScript object 中间的桥梁,
毕竟以 Java 的角度,必须要明确定义 class 有哪些 method 与 field,
所以包一个 class 来满足这个需求。
再来是我们真正要在程式当中使用的 `WebSocket`:
// ======== Java code ======== //
package psmonkey.client.websocket;
public class WebSocket {
private String uri;
private JsWebSocket jsWebSocket;
public WebSocket(String uri) {
this.uri = uri;
}
public void send(String msg) {
jsWebSocket.send(msg);
}
public native void open() /*-{
//在 native method 当中就是写 JavaScript
var websocket = new $wnd.WebSocket(
[email protected]::uri
);
[email protected]::jsWebSocket =
websocket;
//直接复制贴上刚刚的 JavaScript 码
websocket.onopen = function(evnt) { alert("connected!"); }
websocket.onmessage = function(evnt) {
alert("Receive : " + evnt.data);
}
//onerror、onclose 继续跳过 XD
}-*/;
}
关键点在於 `open()` 这个 method。
首先,透过 `this.@PACKAGE_NAME.CLASS_NAME::FIELD_NAME` 这种扭曲的方式,
我们可以在 native method 当中存取 Java code 的 field;
而 `jsWebSocket` 也是用这样的方式 assign 值。
然後我们就可以用很 Java 的方式来作到一开始 JavaScript 程式码的功能:
// ======== Java code ======== //
WebSocket websocket = new WebSocket("ws://localhost:8000/FOO_PATH");
websocket.open();
websocket.send(SOMETHING);
但是这样还不够 Java,
因为 `onopen`、`onmessage` 的行为都还是用 JavaScript 写。
所以接下来要把这部份也给 Java 化
// ======== Java code ======== //
//in class : WebSocket
public native void open() /*-{
//在 native method 当中就是写 JavaScript
var websocket = new $wnd.WebSocket(
[email protected]::uri
);
[email protected]::jsWebSocket = websocket;
var self = this;
websocket.onopen = function(event) {
[email protected]::onOpen()();
}
websocket.onmessage = function(event) {
[email protected]::onMessage(
Ljava/lang/String;)(event.data);
}
}-*/
public void onOpen() {
Window.alert("connected!");
}
public void onMessage(String data) {
Window.alert("Receive : " + data);
}
一样也是 `this.@PACKAGE_NAME.CLASS_NAME::METHOD_NAME` 这种扭曲的方式,
可以在 native method 当中去指定要呼叫的 Java method。
如此一来,後头就都是 Java 的事情了!
.... to be continue
--
钱锺书:
说出来的话
http://www.psmonkey.org
比不上不说出来的话
Java 版 cookcomic 版
只影射着说不出来的话
and more......
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 114.25.4.52
1F:推 johnny1972: 虽然我java很差,不过这篇给了我不少其他灵感,感谢 07/06 11:49
2F:→ PsMonkey:敢问灵感是指? 07/06 12:25
3F:推 tails32100:猴大先推倒再说 [被踹飞] 07/06 15:42
4F:推 gmoz:赞 07/06 20:34
5F:推 cuteclare:膜拜~ 07/06 20:34
6F:推 s3748679:<~ 刚看成: tails32100:先推倒猴大再说 [被踹飞] 07/06 21:34
7F:推 moonjuice:原来还可以这样结合喔@@ 07/06 22:32
8F:推 cuteclare:最後一段code少了-*/; 07/07 17:27
修正 cateclare 提醒的错误以及一些排版
※ 编辑: PsMonkey 来自: 1.162.248.88 (07/08 11:14)