JRubyをライブラリとして使う(Android編)

三行まとめ

  • JRuby 1.7.24 (Ruby 1.9.3相当) はAndroidから普通に使える
  • JRuby 9.1.0.0 (Ruby 2.3相当) はOracle JDK固有のクラスを使っていてAndroidでは使えない
  • JRuby on Androidに興味のある開発チームはいるようなので、しばらくすると使えるようにはなるかもしれない

解説

Re:VIEW のソースを直接レンダリングするAndroidアプリがあったら面白いかなと思い、かといってRe:VIEWをJavaで再実装するのも面倒なのでまずJRubyをAndroidで動かせるかどうか試してみました。といっても、すでにJRubyを処理系として使うRubotoというAndroid用フレームワークがあるので、ある程度は使えることが分かっていました。

このJRubyを試しているリポジトリは https://github.com/gfx/HelloJRuby にあります。

依存関係

まず依存は次のように指定するだけです。ただし、JRubyは9000系と1.7系があり、相当するRubyのバージョンが違います。

// JRuby 9000 (Ruby 2.x相当)
dependencies {
   compile 'org.jruby:jruby:9.1.0.0'
}

// JRuby 1.7 (Ruby 1.9相当)
dependencies {
   compile 'org.jruby:jruby:1.7.24'
}

実行

JRuby wiki に簡単な使い方が書いてありるので、それにしたがってRubyスクリプトを評価するコードを書きます。JITを無効化するのを忘れないように。

from MainActivity.java:

Log.d(TAG, "Ruby starting");

// Rubyランタイムの設定
RubyInstanceConfig rubyInstanceConfig = new RubyInstanceConfig();
// JITはOFFにする
rubyInstanceConfig.setCompileMode(RubyInstanceConfig.CompileMode.OFF);
// Rubyラインタイムを生成する
Ruby runtime = Ruby.newInstance(rubyInstanceConfig);

Log.d(TAG, "Ruby instantiated (" + (System.currentTimeMillis() - t0) + "ms)");

// Rubyスクリプト。android.util.Log.d()を呼んだあと、BuildConfig.APPLICATION_IDを返す
String script = "require 'java'\n"+
        "Java::AndroidUtil::Log.d %Q{Ruby}, %Q{This is JRuby (Ruby #{RUBY_VERSION})}\n"+
        "Java::ComGithubGfxAndroidHellojruby::BuildConfig::APPLICATION_ID";

// Rubyスクリプトを実行する
IRubyObject result = runtime.evalScriptlet(script);

Log.d(TAG, "Ruby evaluated (" + (System.currentTimeMillis() - t0) + "ms)");

TextView textView = (TextView) findViewById(R.id.text);
assert textView != null;
// RubyオブジェクトのもつJavaオブジェクトを取り出す
textView.setText(result.toJava(String.class).toString());

さて、これをJRuby 9.1.0.0で動かすと、java.lang.invoke.SwitchPoint が存在しないという実行時例外で落ちてしまいました。これはAndrodには実装されていないようです。そこで一旦JRubyのバージョンを1.7.24に落としてみると、無事に実行できました。

Re:VIEWの最小サポートバージョンがRuby 2.0 なので Ruby 2.2に相当するJRuby 9.1.0.0を使いたいところです。現在は実行できない以上、当初の目的であったRe:VIEWの実行はできませんね。残念…。