OCamlベースのAndroidアプリに向けて
O'Caml on Android というパッチを作っている. 柔軟かつ信頼性の高いOCamlでAndroidのアプリを書くのが目標だ. ここ2ヶ月ほどocamljs (OCamlからJavaScriptへのコンパイラ) を使っていて、OCamlの信頼感と柔軟性にとても満足したので,ではAndroidでもOCamlだ、とばかりに開発を再開している.
iPhoneやiPadのObjective-Cと違い,AndroidではJavaの型の扱いやJVMとの相互作用が必要でありなかなか厄介だ. しかしiPhoneではいくつかの開発 事例もあるのでAndroidでもそれなりに有効ではないかと信じている.がんばりたい.
OCamlを使う利点
もはや自分の中では言い古した感があるのだけど,念のため列挙してみる.
信頼性
OCamlには null値がない.このため NullPointerExceptionが起こらない.さらに,代数的データ型が使えるため パターンマッチによる場合分けの 網羅性検査 がある. Javaにも型システムがあるけれど, 全ての参照型でnull値が許される点と,nullチェックの抜け漏れが容易に起こり得る点でよろしくない.
OCamlを経由することの欠点
欠点がないわけではない.
マルチスレッドの扱い
OCamlのGCは並行でない. もしAndroidがマルチコア化される時代が来れば,パフォーマンス上の問題になる.またOCamlのコードを同時に実行できるスレッドは1つだけであり,途中でOCamlスレッドのコンテキストスイッチが発生しないケースがあるかもしれない(過去の日記参照;ほとんどないと思うけどAndroidで調べてない).
OCamlとJavaの連携: 4通りの方法
AndroidアプリはJavaベースなので, Javaから OCaml を実行するには次の2つの軸から4通りが考えられる:
- JVM or JNI ?
- ocamlc or ocamlopt ?
私がつくっているO'Caml on Android (名前を付けたい…)は、Android NDKを使うので (B-1) か (B-2) だ. 一方,Cadmium を使った OCaml on Androidは JVM上でOCamlバイトコードを解釈実行するので(A-1)だ.
JNIを使った OCaml-Java連携: 3つのレベル
JNIを経由したOCamlとJavaの連携には,洗練度により,次の3つのレベルがある:
下の項目は上の項目に依存しているので,順に解説する.
原始的な方法
JNI経由でOCamlを呼ぶには,まず JNIで呼び出す関数をエクスポートした共有ライブラリ(AndroidはLinuxベースなので .so)を作成し,Java側で System.loadLibrary() でロードする.(OCaml-Nagoyaのwikiにも書いてみた)
そこで まず私は OCaml on Android という, .so を生成できるコンパイラを準備した. ビルドには Android NDKが必要である.
あまり細かいことはできないが,例えば OCamlのトップレベルを Android アプリとして動かせるようになる(なった).
camljava を使う
camljava は OCaml のモジュール Jni を提供しており,ここから JNIの関数をだいたい叩ける.しかし camljava を Android で使うにはいくつかの障壁がある(O'jacareも同じ問題がある)
- camljava は main関数が OCaml側にあるのが前提で,そのままでは使えない
- メモリ管理系の関数が欠落
- DeleteLocalRef がない. 多くのクラスやオブジェクトを参照すると local reference tableがいっぱいになる
- Androidのバグ.私がレポートしたものは
- 親インタフェースのメソッドを子インタフェース経由でGetMethodIDできない
- http://code.google.com/p/android/issues/detail?id=13771
- O'jacareが生成するコードが例外で止まる
- コンストラクタをCallNonvirtualVoidMethodで呼ぶと例外(NewObjectで呼べばOK)
- http://code.google.com/p/android/issues/detail?id=13832
- このためコールバック用スタブ(Callbackクラス)の初期化に失敗する
- 親インタフェースのメソッドを子インタフェース経由でGetMethodIDできない
現在パッチを書いている.手元で大部分の修正はできているが,メモリ管理周りはまだ自信がない.
O'Jacareを使う
O'Jacare は, camljava をベースに作られている.Javaのクラスを OCamlのクラスとして扱えるのが利点だが, Javaはメソッド名のオーバーロードを許すがOCamlでは許さないといった差異があるため, スタブの生成には IDLファイルを準備する必要がある.
Java のクラスについて宣言した .idl ファイルを ojacare コマンドに与えると, OCaml 側のスタブと Java側のスタブが生成される.OCamlのクラスとして, Javaのクラスを扱える. OCaml側でこのクラスを inherit すれば,Javaのクラス/インタフェースを拡張/実装でき,コールバックできる
しかし,ざっくり Android のクラスを ojacare にかけてみたところ,次の問題をみつけた.(私の備忘録として…)
- (マニュアルに記載あり)Java側の参照を開放しない.
- 型システムの制限
- メソッド名の問題 (軽症).
- O'jacareビルド時の camlp4の非互換性.
- camljavaと同様の問題.
- クラスへの参照をキャッシュしてしまうがメソッド終了時に参照が失われる…
- 多くのクラスをIDLに含めると,起動時に local reference tableの上限(たった512!)を越えて findClass等をするので落ちる
- O'jacare の問題.
- (修正済み) src/check/type.ml で Ishort -> Cbyte となっているのは Ishort -> Cshort
- int が camlint にマップされてしまう. R.layout.main などは 扱えない
型システムの制限はやや厳しい.また,メモリ管理周りも改善しないと使えない.