GWT
符合下列條件的 class 才能在 GWT 內使用:
- primitive data type
- array(陣列元素也必須 compatible)
- enum
- JRE emulation class(包含可用 method)
- 繼承 compatible class
- 所有 field 都符合 compatible 原則
另外,
synchronized
與 finalize()
加了不會出問題,但是 GWT 會直接忽略。(沒有考慮撰寫 emulate class 的招數)
reference:
- 滿足 compatible 規範
- implement
IsSerializable
或是Serializable
- 如果是某個 class 的 inner class,則必須得是 static class(可以不是 public)
- 有 default(沒有參數)constructor,任何 access modifier 均可(private 也無所謂)。或是根本沒有任何 constructor。
- 所有 field 都符合 RPC 原則。
- 例外:final field(不會在 GWT RPC 中傳遞)
- 例外:transient field(接收端會得到 null)
(沒有考慮 custom serialization 的招數)
備註:enum 只傳遞名稱。例如
FooEnum.FOO
,就只會傳 FOO
,裡頭的 field 值一概忽略。reference:
假設
Foo
是符合 RPC 規範的 class,fooList
是 ArrayList<Foo>
,- Collections.unmodifiableList(fooList)
- fooList.subList()
上述是因為回傳值的 instance 的 class 不符合 RPC 規範,所以無法過 GWT RPC。
- 預設設定下,在 production mode 的 assert 是不會被觸發的。
when-type-is
的 class 沒有限定一定要 interface(官方文件好像也沒特別註明), 目前測試用 abstract class 也沒有問題。$wnd
是對應到 JS 的 window
,也就是說 JSNI 的 $wnd.alert()
就等於 JS 的 window.alert()
。 如果需要 JS 的全域變數,基本上就只能讓 $wnd
多加一個變數來處理, 實際上這等同在 host page 的 <script>
宣告一個變數://host page script block
var value = "foo";
const wtf = "wtf"; //用 const 宣告會取不到值,原因不明...... Orz
//in JSNI
$wnd.newGlobalArray = []; //JS 可以亂加 field 不要意外,延伸出來的 JS 哏這裡略過不提
$wnd.alert($wnd.value); //alert 視窗的訊息會是「foo」
$wnd.alert($wnd.wtf); //alert 視窗的訊息會是「undefine」
$doc
是對應到 JS 的 document
。 注意,如果在 JSNI 裡頭直接操作 document
,並不會是 host page 的 document
instance, 而是 host page 裡頭某個 iframe 裡頭的 document
。 這背後機制暫時不明... [死]在 JSNI 裡頭用
[]
產生一個 class array 作為回傳值,基本上是不可行, 因為(以 Java 的觀點)缺乏 class 的資訊,而且到了執行期才會真正炸 cast 錯誤。 目前找到最簡單的方法就是自己用 Java 重新包一次... ==",例如://目的:不是要取得整個 entry,而是只要 entry 裡頭的 resource
public native FooJSO[] errorResult() /*-{
var result = [];
for (var i = 0; i < this.entry.legnth; i++) {
result.push(this.entry[i].resource);
}
return result;
}-*/;
// ======== //
private final native Foo result(int index) /*-{ return this.entry[index].resource; }-*/;
public List<FooJSO> getResult() {
ArrayList<FooJSO> result = new ArrayList<>();
for (int i = 0; i < getSize(); i++) {
result.add(result(i));
}
return result;
}
client side 的 GWT 將 JSON 字串轉回(偽)Java instance 的議題。
注意 ######請不要忘記這是 JS、這是 JS、這是 JS(很重要所以要講三次), 所以隨便 casting 是很合理的(謎之聲:教練我想寫 Java [淚目])。
Overlay Type 的基礎是
JavaScriptObject
。 繼承 JavaScriptObject
的 class 必須:package
等級的 class modifier- 有
protected
等級的 default constructor - 不能有 instance field
- 要嘛 class 宣告成 final,要嘛每個 method(即使不是 native)宣告成 final
主要是用 JSNI 的 getter:
public final native String getId() /*-{ return this.id; }-*/;
對一個 JSON 字串作
JsonUtils.safeEval()
就會得到 JavaScriptObject
或其子孫,端看泛型怎麼指定。 後續呼叫 JavaScriptObject.cast()
也可以轉成想要的(繼承 JavaScriptObject)的 class。轉換 tip ####
- 務必在 production mode 作實際測試,會有 SDM 與 production mode 結果不同的情況。
- 無法作 autoboxing
- 所以即使
T
給 primitive type 的 wrapper,仍然無法用泛型public native <T> getField(String fieldName) /*-{ return this[fieldName]; }-*/;
。
- primitive type、enum(名稱一樣應該就 OK)可以直接轉換。
- 無法轉換
long
,compiler 會報錯,用Long
就沒問題 - 但是這很容易導致一個炸點,在實際使用時還是有可能被當成字串,例如:public final native int fakeInt() /*-{return this.foo == "" ? 0 : this.foo;}-*/;public final native Integer realInt() /*-{return @java.lang.Integer::valueOf(Ljava/lang/String;)(this.foo == "" ? "0" : this.foo);}-*/;public final void test() {Console.log((fakeInt() + 1) + " != " + (realInt() + 1));}呼叫
test()
會得到如「11 != 2」這種結果。 所以保險起見還是統統 拿去作雞精 轉成標準 class instance。 注意: 只有 production mode 才會炸,SDM 結果正常。
Date
轉換:private final native String fooDate() /*-{ return this.fooDate; }-*/;public final Date getFooDate() {//JSON 的 datetime 標準據說是用 ISO-8601//但是真的用這個 formate 卻無法處理如 "2012-12-21" 這種只有前半段符合的字串 =="//return DateTimeFormat.getFormat(PredefinedFormat.ISO_8601).parse(fooDate());//所以... 直接用 Date constructor,底層實作是直接用 JS 解... Orzreturn new Date(fooDate());//當然也可以直接寫在 JSNI 裡頭,不過寫起來好囉唆... [蓋牌]}
炸點 ####
假設收到這樣一個 JSON 字串
["ID", "DATA"]
然後這段程式碼就會炸奇怪的問題:
HashMap<String, JavaScriptObject> map = new HashMap<>();
JsArrayMixed array = JsonUtils.safeEval(json);
map.put(array.getString(0), array.getObject(1)); //這邊不會有事
JavaScriptObject foo = map.get(array.getString(0)); //這行會炸 casting 問題
WtfJSO wtf = new WtfJSO(array.getObject(1)); //constructor 參數的型態也是 JavaScriptObject
//上面這樣就不會出問題... WTF?
用 UiBinder 弄畫面,執行時一直炸找不到 fooWidget class 的錯誤(但是 Eclipse 沒有 compile error), 先單純用程式 new FooWidget() 出來,通常就會知道 FooWidget 錯在哪裡了。 (2.5.1 時常見不小心用了 generic 的
<>
就炸了,但是 Eclipse 不會炸錯誤 Orz)在
gwt.xml
當中要加上有要實作的語系 <extend-property name="locale" values="zh_CN"/>
至於設定 default locale 沒啥用
<set-property-fallback name="locale" value="en"/>
個人覺得沒啥用,倒是指定了語系就得要有
extend-property
然後也找得到對應的 properties 檔。 而且 default 的 properties 檔案還是不能少...... =="掛
<collapse-all-properties />
好像不受影響(還是我誤會了這個設定值的意義...... Orz)內建原生機制無法徵測 browser 的語系,必須在 host page 給
<meta>
或是在 URL 掛上 query string
http://127.0.0.1/hostPage.html?locale=zh_CN
會優先使用 query string 的設定值。
在 SDM 下,加上 query string 之後、甚至重新 compile 好像都不會順利切換語系。 另外開一個新的 tab 測試會比較實在。
用
ImageResource
的 Image
如果要調整大小, new Image(imgResource); //size 太大會留白
new Image(imgResource.getSafeUri()); //OK
沒有 attach 就不會觸發
LoadEvent
,不過即使 setVisible(false)
也沒關係。- 2011 年
- Ray Cromwell 列出使用 GWT 的 Google Product。
- 2013 年:
- 新版的 Google Drive 的 Sheet 使用 GWT 撰寫
- Ray Cromwell 在 Gwt.Create 報告 Google Drive 使用 GWT 撰寫的投影片(p.6)
- 2014 年:
- Google Inbox、Google Calendar 是 GWT(70%)+ Closure(30%)
- 詭異的 Google 官方網站,證實 Google Document 使用 GWT
註:Ray Cromwell 是 Googler、現任 GWT 委員長。
Last modified 3yr ago