GWT:JsInteropの使い方、その1
もちろん、GWTには以前からJavaScriptのメソッドを呼び出したり、コールバックを受ける仕組みがあったのだが(JSNI)、JsInteropによってよりシームレスになっているという。現状で特に不便は無いが、使ってみようと思う。しかし日本語の資料が一切無く、また英語であっても意味不明なものが多いため、ここでまとめておく。なお、GWT2.8.2を使用している。
JavaのオブジェクトをJavaScriptでJSON化し、コンソールに出力してみる
最初にとても単純な機能を作ってみる。Java側で定義したクラスのオブジェクトをJavaScriptの機能を使ってJSON文字列にし、それをコンソールに出力する。
JavaのクラスをJavaScript側に渡せるようにするためには、JsTypeというアノテーションをつける。
@JsType
public static class Sample {
public int a, b;
}
JSON側のオブジェクトをJavaのクラスとして定義してやる。
// JavaScript側のグローバル名前空間にある"JSON"オブジェクト
@JsType(isNative = true, namespace = JsPackage.GLOBAL, name="JSON")
public static class MyJSON {
public static native String stringify(Object obj);
public static native Object parse(String obj);
}
// JavaScript側のグローバル名前空間にある"window"オブジェクト
@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "window")
public abstract static class MyWindow {
public static MyConsole console;
public static native void alert(String s);
}
// これはMyWindowの中の"console"という名前のフィールドの型を定義するらしい
// ここにはnamespace=、name=は不要らしい
@JsType(isNative = true)
public interface MyConsole {
void log(Object... o);
}
処理は次のようなものだ。コンソールにJSON文字列を出力し、アラートダイアログを出す。
Sample sample = new Sample();
sample.a = 123;
sample.b = 456;
MyWindow.console.log(MyJSON.stringify(sample));
MyWindow.alert("this is test");
問題点とその解消
基本的に、GWTではJavaコードをJavaScriptコードに変換する際に、内容を難読化してしまう。そのため、「名前を保持しろ」という指示をしてやらねばならないようだ。そうでないと、以下のようなJSON文字列になってしまう。
{"a_1_g$":123,"b_1_g$":456}
これを解消するには、開発環境のSuper Dev Modeと本番環境用のコンパイルではやり方が異なる。
Super Dev Modeの場合
Super Dev Modeのコードサーバを起動する際に以下のフラグを指定しないといけない。
-generateJsInteropExports
例えば以下のように指定する
-workDir C:\Users\admin\Desktop\gwt-tmp -logLevel INFO -port 9876 -generateJsInteropExports foo.bar.Sample
本番用コンパイルの場合
この場合もコンパイラに指示をしてやる。GWTコンパイラには上記と同じフラグを指定すれば良いかと思うが、特にGradleプラグインを使う場合は以下が必要なようだ。
※プラグインとしては、「’org.wisepersist:gwt-gradle-plugin:1.0.13’」を使用している。
gwt {
gwtVersion = '2.8.2'
jsInteropExports {
generate = true
}
}
あるいは各モジュールのコンパイルで以下とする。
task makeModule(type: GwtCompile, dependsOn: classes) {
modules = ['foo.bar.Sample']
minHeapSize = "512M"
maxHeapSize = "1024M"
jsInteropExports.setGenerate true
}