概要
ClojureのGradle用のプラグインとして、gradle-clojureというものが存在する。私はそのgradle-clojureをベースにAndroidのビルド用の機能を作成しており、本記事ではその紹介を行う。
現状はバージョン0.1くらいの完成度なので実用段階ではないが、試してフィードバックをいただけると嬉しい。
Clojure on Androidの現状と問題点
実は既に、Clojure on Androidというプロジェクトが数年前から存在し、AndroidでClojureを使う場合は、Clojure on Androidプロジェクトの成果物を使うのが定番となっている(React Native + clojurescriptを使う方法もあるが、ここでは言及しない)。
Clojure on Androidでは、leiningen + lein-droidプラグインを使ってビルドを行う仕組みなのだが、現状以下の問題点がある。
- Androidのビルドシステムの機能をlein-droidで独自に実装し直しているので、Androidビルドシステムの変化に追従するのにコストがかかる。
- 開発が止まっている。
(しかも、最新のlein-droidはこのパッチを当てないとビルドができない。) - Androidの標準ビルドシステムはGradleなので、整合性が悪い。
そこで、gradle-clojureにAndroid用のビルド機能を追加することにした。
Android Studioでビルドできるサンプルアプリ
gradle-clojureのAndroid対応と、そのAndroid対応版でビルドできるサンプルプロジェクトを用意した。
$ git clone https://github.com/anolivetree/gradle-clojure-android-sample.git
サンプルプロジェクトをビルドし実行すると、以下のような表示になる(はず)。
アプリを起動すると、http://www.yahoo.co.jp から(Clojureのライブラリである)http-kitを使ってデータを取得し、TextViewに表示する。上部のGETボタンを押すと、Logcatに"hello"と教示される。
もしClassNotFoundExceptionでActivityの起動ができない場合は、
- targetSdkVersionを下げる
- ^によるtype hintを付けまくる
と、解決するかもしれない。
nREPL
サンプルアプリにはnREPLを組み込んでいて、アプリの起動後数秒でnREPLが接続可能となる。nREPLを起動するためのソースは、nekoのソースを借用している。
nREPLに接続するには、
$ adb forward tcp:9999 tcp:9999
としたのち、lein replで9999ポートに接続すると、REPLが動く。
$ lein repl :connect 9999
Connecting to nREPL at 127.0.0.1:9999
REPL-y 0.3.7, nREPL 0.2.10
Clojure 1.7.0
Dalvik 0.9
FileNotFoundException Could not locate clojure/java/javadoc__init.class or clojure/java/javadoc.clj on classpath. clojure.lang.RT.load (RT.java:462)
#object[clojure.lang.Namespace 0xd6f2923 "user"]
Error loading namespace; falling back to user
nil
user=> (+ 1 2)
3
user=>
nREPLは軽い動作確認しかしていないので、もしかしたらちゃんと動かないかもしれない。
今後の課題とTODO
たくさんある。
Clojureのバージョンが古い
Clojure on Androidプロジェクトで、素のClojureにAndroid対応を行ったClojureを提供しているが、バージョンが1.7.0と古い。Android用の変更を反映した1.8(もしくは1.9)を用意したい。
クラスの動的なロードが遅い
クラスの動的なロード時に
- ClojureのコンパイラがJava bytecodeを作成
- dxでDalvik bytecodeに変換し、ロード
としているためなのだが、非常に遅い。将来的には直接Dalvikのbytecodeを出力したい。
JavaからClojureで作成したクラスの呼び出しができない
現状は先に.javaをコンパイルし、次に.cljのコンパイルを行っている。
そのため、ClojureからJavaの呼び出しは可能だが、逆はできない。
library project, test projectの対応
試していないが、きっと動かないと思う。
未使用ファイルもすべてAOTということはできない。
lein-droidにある、:aot :all-with-unused相当のことはできない。