JavaのソースコードからJavaScript / Objective-Cのソースコードを生成する

JavaのソースコードからJavaScript / Objective-Cのソースコードを生成する

  • このエントリーをはてなブックマークに追加

はじめまして。フロントエンドエンジニアのらくさんです。

弊社は、Flash Lite 1.1のSWFをスマートフォンで再生できるようにする、JswfPlayerというサービスを提供しています。私はそのプレーヤー開発を行っています。

JswfPlayerはWebブラウザ上で動作するため、ネイティブアプリで使用するにはWebView内で動かすことになりますが、せっかくネイティブアプリにするならJswfPlayerもJavaやObjective-Cでコードを書きOpenGLで描画を行い高速に動作させたくなります。

しかし、JavaScript/Java/Objective-Cそれぞれにフルスクラッチで開発するのは大変です。そこで、コードを出来るだけ共有する方法を模索しており、最近GWT-Exporterとj2objcを試してみたので使用方法をまとめてみました。(ちなみに、JswfPlayerのネイティブ版を開発する計画は今のところありません…)

GWT-Exporterとは

GWTはご存知の方も多いと思いますが、Google Web Toolkitの略称で「Javaを使ってウェブ用Ajaxアプリケーションを開発できるオープンソースのJavaソフトウェア開発フレームワーク」(Wikipediaより) です。Javaで書いたソースコードのうち、クライアント側で動作するものについてはJavaScriptに変換されて実行されます。

GWT-Exporterは、GWTでJavaScriptに変換されたコードを手書きのJavaScriptから利用できるようにするためのGWTモジュールです。今回はこのGWT-Exporterを使ってJavaからJavaScriptに変換することだけが目的なので、通常のGWTを用いた開発については触れません。

■GWT-Exporterのプロジェクトサイト
http://code.google.com/p/gwt-exporter/

j2objcとは

j2objcは、JavaのソースコードをObjective-Cに変換するツールで、2012年9月にGoogleからリリースされたばかりのものです。まだ開発途上であり頻繁に更新されているようですが、Google内のいくつかのプロジェクトでは使用されているようです。

続きを読む

■j2objcのプロジェクトサイト
http://code.google.com/p/j2objc/

GWT-Exporterの使い方

GWTのインストール

GWTの開発環境はEclipseのプラグインとして提供されているので、Eclipseが必要です。この記事ではEclipse3.7を使用しています。

GWTのインストール方法は割愛します。
私は次のページを参考にしました。

Eclipse を用いた GWT 開発環境の構築方法 – Google Web Toolkit (GWT) 入門

GWTプロジェクトの作成

Fileメニューから「New」→「Other…」を選び、Googleフォルダ内の「Web Application Project」を選択します。
GWTプロジェクトの作成

プロジェクト名とパッケージ名を入力し、「Use Google App Engine」と「Generate project sample」は不要なのでチェックを外します。Finishボタンを押すとGWTプロジェクトが作成されます。
プロジェクト名とパッケージ名を入力

GWT-Exporterのダウンロードとビルドパスの設定

http://code.google.com/p/gwt-exporter/downloads/list
ここから gwtexporter-2.4.0.jar をダウンロードします。

ダウンロードした gwtexporter-2.4.0.jar をプロジェクトに追加し、ビルドパスに設定します。

GWT-Exporterを使用するための設定

Fileメニューから「New」→「Other…」を選び、Google Web Toolkitフォルダ内の「Module」を選択します。
GWT-Exporterを使用するための設定

パッケージ名にはプロジェクト作成時に指定したものを入力します。モジュール名は適当な名前を付けます。Inherited modulesにデフォルトで指定されている「com.google.gwt.user.Uesr」を取り除き、「org.timepedia.exporter.Exporter」を追加します。
パッケージ名

「モジュール名.gwt.xml」というファイルが作成されるので、それを開き、次の3行を追加します。

	<set-property name="export" value="yes"/>
	<entry-point class="モジュールのパッケージ名.client.適当なクラス名"/>
	<add-linker name="xs"/>

モジュール名.gwt.xml

上記のentry-pointで指定したクラスを、次の内容で作成します。

  • EntryPointインターフェースを実装する
  • onModuleLoadメソッド内でExporterUtil.exportAll()を呼ぶ
package com.sonicmoov.example.gwtx.client;

import org.timepedia.exporter.client.ExporterUtil;
import com.google.gwt.core.client.EntryPoint;

public class ExportAll implements EntryPoint {

	@Override
	public void onModuleLoad() {
		ExporterUtil.exportAll();
	}

}

Javaのソースコード

JavaScriptから呼び出したいクラスは、Exportableインターフェースを実装している必要があります。さらに、@Exportアノテーションをクラスまたはメソッドに指定する必要があります。

また、JavaScript側でのパッケージ名を@ExportPackageアノテーションで指定することができます。

詳しくは下記をご覧ください。
http://code.google.com/p/gwt-exporter/wiki/GettingStarted

package com.sonicmoov.example.gwtx.client;

import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.ExportPackage;
import org.timepedia.exporter.client.Exportable;

@ExportPackage("")
@Export
public class Hello implements Exportable {

	public String sayHello() {
		return "HELLO!!";
	}
}

既存のJavaコードがあり、それに手を入れたくない場合は Exportable の代わりに ExportOverlay を使うこともできます。今回は後述のj2objcを使用する際にこの方が都合が良いので、こちらを採用します。

package com.sonicmoov.example.gwtx.client;

public class Hello {

	public String sayHello() {
		return "HELLO!!";
	}
}
package com.sonicmoov.example.gwtx.client;

import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.ExportOverlay;
import org.timepedia.exporter.client.ExportPackage;

@ExportPackage("")
@Export
public abstract class HelloOverlay
		implements ExportOverlay<Hello> {

	public abstract String sayHello();

}

JSNI (JavaScript Native Interface)

Java側からJavaScriptを呼ぶことができます。JNIと同様にnative修飾子を付けたメソッドを宣言し、次のように /*-{}-*/ で囲ったコメント内にJavaScriptのコードを記述します。

package com.sonicmoov.example.gwtx.client;

public class Hello {

	public String sayHello() {
		return "HELLO!!";
	}

	public native void sayHelloNative() /*-{
		alert("HELLO Native!!");
	}-*/;
}

オーバーレイクラスの方も修正します。

package com.sonicmoov.example.gwtx.client;

import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.ExportOverlay;
import org.timepedia.exporter.client.ExportPackage;

@ExportPackage("")
@Export
public abstract class HelloOverlay
		implements ExportOverlay<Hello> {

	public abstract String sayHello();
	public abstract void sayHelloNative();

}

コンパイル

Package Explorer内のプロジェクトを右クリックし、「Google」→「GWT Compile」を選択するとダイアログが出てきます。そのままCompileボタンを押してください。
コンパイル

しばらく待つと次のようなファイルが出来上がります。
ファイルが出来上がり

JavaScriptから呼び出す

コンパイルしてできた「***.nocache.js」というファイルをscriptタグで読み込むと、@Exportアノテーションを指定したクラスやメソッドが使用できます。

<html>
<head>
<script type="text/javascript" src="war/com.sonicmoov.example.gwtx.export/com.sonicmoov.example.gwtx.export.nocache.js"></script>
</head>
<script type="text/javascript">
function sayHello() {
	var hello = new Hello();
	alert(hello.sayHello());
}
function sayHelloNative() {
	var hello = new Hello();
	hello.sayHelloNative();
}
</script>
<body>
<input type="button" onclick="sayHello()" value="sayHello">
<input type="button" onclick="sayHelloNative()" value="sayHelloNative">
</body>
</html>

j2objc

j2objcのビルド

http://code.google.com/p/j2objc/downloads/list
ここからj2objcのソースコードをダウンロードします。バイナリ版もありますが、私の環境では正常に動作しませんでした。

ビルドには次の環境が必要です。

  • Xcode 4以上
  • Java for OS X
  • Apache Maven

次の手順でビルドします。

$ unzip j2objc-0.5.6-src.zip
$ cd j2objc-0.5.6
$ make dist
 

Javaのソースコード

GWT-Exporterの解説で使用したHelloクラスにmainメソッドを追加します。また、JSNIで記述していたsayHelloNativeメソッドも書き換えます(詳細は後述)。

package com.sonicmoov.example.gwtx.client;

public class Hello {

	public String sayHello() {
		return "HELLO!!";
	}

	public native void sayHelloNative() /*-{
		alert("HELLO Native!!");
	}-*/ /*-[
		NSLog(@"HELLO Native!!");
	]-*/;

	public static void main(String[] args) {
		Hello hello = new Hello();
		String str = hello.sayHello();
		System.out.println(str);
		hello.sayHelloNative();
	}

}

j2objcでObjective-Cに変換、j2objccでコンパイル

次のコマンドで上記のHelloクラスをObjective-Cに変換します。

$ j2objc -d objc src/com/sonicmoov/example/gwtx/client/Hello.java

このコマンドによりobjcディレクトリ内(のcom/sonicmoov/example/gwtx/client)に Hello.h と Hello.m が作成されます。

//
//  Generated by the J2ObjC translator.  DO NOT EDIT!
//  source: src/com/sonicmoov/example/gwtx/client/Hello.java
//
//  Created by rakusan on 12/12/10.
//

@class IOSObjectArray;

#import "JreEmulation.h"

@interface ComSonicmoovExampleGwtxClientHello : NSObject {
}

- (NSString *)sayHello;
- (void)sayHelloNative;
@end
//
//  Generated by the J2ObjC translator.  DO NOT EDIT!
//  source: src/com/sonicmoov/example/gwtx/client/Hello.java
//
//  Created by rakusan on 12/12/10.
//

#import "IOSObjectArray.h"
#import "com/sonicmoov/example/gwtx/client/Hello.h"

@implementation ComSonicmoovExampleGwtxClientHello

- (NSString *)sayHello {
  return @"HELLO!!";
}

- (void)sayHelloNative   {
    NSLog(@"HELLO Native!!");
  }

@end

int main( int argc, const char *argv[] ) {
  int exitCode = 0;
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  IOSObjectArray *args = JreEmulationMainArguments(argc, argv);

  ComSonicmoovExampleGwtxClientHello *hello = [[[ComSonicmoovExampleGwtxClientHello alloc] init] autorelease];
  NSString *str = [((ComSonicmoovExampleGwtxClientHello *) NIL_CHK(hello)) sayHello];
  NSLog(@"%@", str);
  [((ComSonicmoovExampleGwtxClientHello *) NIL_CHK(hello)) sayHelloNative];

  [pool release];
  return exitCode;
}

次のコマンドで Hello.h, Hello.m から実行バイナリ hello を生成します。

$ j2objcc -I objc -o hello objc/com/sonicmoov/example/gwtx/client/Hello.m

出来上がったバイナリを実行します。

$ ./hello
2012-12-10 18:25:45.593 hello[25504:60b] HELLO!!
2012-12-10 18:25:45.595 hello[25504:60b] HELLO Native!!

OCNI (Objective-C Native Interface)

さきほど、JSNIで記述していたsayHelloNativeメソッドを次のように書き換えました。

	public native void sayHelloNative() /*-{
		alert("HELLO Native!!");
	}-*/ /*-[
		NSLog(@"HELLO Native!!");
	]-*/;

j2objcは、ネイティブメソッド用のコメントデリミタにGWTのJSNIとは異なる /*-[]-*/ を使用することで、Objective-C、GWT、そしてJava(JNI)で同じネイティブメソッドを共有できるようになっています。

メモリ管理について

JavaからObjective-Cに変換する場合、メモリ管理がどのようになるのか気になるところです。j2objcでは、明示的なリファレンスカウント、ARC、GCの3つから選べるようです。デフォルトは明示的なリファレンスカウントです。

詳細は下記をご覧ください。
http://code.google.com/p/j2objc/wiki/MemoryManagement

まとめ

GWT-Exporterとj2objcの基本的な使い方は以上になります。JavaからJavaScript/Objective-Cに変換できるのは、java.langパッケージとjava.utilパッケージの一部のみのようなので、GWT-Exporterやj2objcがどんなプロジェクトでも使用できるわけではありませんが、ビジネスロジックをJavaで記述しJavaScript/Objective-Cに変換、UI等はそれぞれに書くことで、Webアプリ、Android、iOS間で一部のソースコードを共有できるようになるのではないでしょうか。

今回は非常に単純なコードで試しただけですが、もっと大きく複雑なものでのテストも行いパフォーマンスの計測等も行う予定です。またj2objcはXcodeに統合して使うこともできるようなので、それについても調査し改めて記事にしたいと思います。

  • このエントリーをはてなブックマークに追加

記事作成者の紹介

らくさん(フロントエンドエンジニア)

フロントエンドエンジニアのらくさんです。JswfPlayerという弊社サービスでFlash再生エンジンを開発したり、動画広告Guileの動画プレーヤーを開発したりしています。

関連するSONICMOOVのサービス

フロントエンドエンジニア募集中!

×

SNSでも情報配信中!ぜひご登録ください。

×

SNSでも
情報配信中!
SONICMOOV Facebookページ SONICMOOV Twitter
フロントエンドエンジニア募集中!

新着の記事

mautic is open source marketing automation