Groovy on Android, 技術詳細


この投稿は、[GR8Conf Europe](http://gr8conf.eu/#/) において、Android 上での Groovy の動作に関して発表を行った Cédric Champeau 氏の[ブログ記事](http://melix.github.io/blog/2014/06/grooid2.html)の和訳です。氏のブログは BY-NC-SA ライセンスで公開されているため、本投稿もライセンスを継承します。

---

*翻訳ここから*

前回の[投稿](http://melix.github.io/blog/2014/06/grooid.html)で、私は [Groovy](http://groovy.codehaus.org/)をどのように Andrpod アプリケーションの開発に用いるのか説明しました。この投稿では、それが内部的にどのように動作しているのかより詳しく説明し、なぜこのようなことができるのかヒントを与えます。

## Groovy のコンパイル

Groovy は、バイトコードにコンパイルされる JVM 言語です。Groovy ではスクリプティングも可能ですが、常にコンパイルが行われます。二つの選択肢があるということです。ひとつは、 `.class` ファイルにコンパイルして、JVM 上で他のクラスラファイルと同様に取り扱うこと。これには `javac` の代わりに `groovyc` を使います。もうひとつは、スクリプティング時などに使う、 **ラインタイム** でのクラスのコンパイルです。後者では、アプリケーションの実行中、Groovy が提供する API を使って、ランタイムでソーススクリプト（これはスクリプティングが必須ではない一般の Groovy ソースも含む）が扱えるということです。ひとつ覚えておいて欲しいのは、Groovy はインタプリタ言語 **ではない** ということです。全てはバイトコードとなります。

### 古典的な Android アプリケーション

Dlvik VM は JVM と全く同じバイトコードを使うわけではないため、Android では Java をコンパイルして実行するために若干の手順が発生します。 *dex* と呼ばれるツールは、JVM のバイトコードを Dalvik のバイトコード `classes.dex` に変換します。これを示したのが以下の図です。

![Classic Android Application](http://melix.github.io/blog/2014/06/compilation_process_normal.png)

私達のケースでは Gradle ビルドツールを使います。これは Android プロジェクトにおいて現在標準となっているもので、 Gradle はこのビルドチェーンを実行してくれます。Java ソースは `.class` ファイルにコンパイルされ、次に `dex` ツールでバイトコード `classes.dex` に変換されます。いくつかのステップ（パッケージのサイズ容量を削減する ProGuard など）を経て、アプリケーションの配布形式である APK が生成されます。デバイスにデプロイされれば、残りの作業はクラスファイルを読み込み実行するだけです。

Groovy アプリケーションではこれとどのように違うのか見ていくことにしましょう。

### Groovy でのアプリケーションの記述

このケースでは、私達は Groovy のソースコードである `.groovy` ファイルを使います。`.java` ファイルも同時に使うこともあるでしょう。プロセスとしては非常によく似たものになります。

![Groovy on Android](http://melix.github.io/blog/2014/06/compilation_process_groovy.png)


このスキーマは、 [GR8Conf Agenda](https://github.com/melix/gr8confagenda) で、アプリケーションがいかにしてコンパイルされるかを示したときに使ったイラストです。全てはコンパイル時に実行され、ランタイム時に実行されるものはありません。Groovy ソースは JVM バイトコードにコンパイルされ、`dex` を用いて Dalvik バイトコードにコンパイルされます。このケースでは、 Java との差分はなく、Groovy と Java で同様のプロセスでコンパイルを行うことが出来ます。

ひとつ注意しなければならないことは、すでに数人から報告されていることなのですが、`classes.dex` で使われるメソッドディスクリプタがピュア Java で記述されるアプリケーションより非常に多いということです。早期のテストから、Groovy のアプリケーションは最適化抜きで 8k ものメソッドを消費することがわかっています。`classes.dex` のリミットは 65536 メソッドなので、これは気に留めておく必要がある事項です。私は Android アプリケーションのプロフェッショナルというわけではありませんが、 `multi-dex` オプションのようなものを使うことができるのではないかと思っています。

結局、Groovy はアプリケーションに組み込まれるその他のライブラリ、すなわち *単なる* 追加の jar ファイルと変わりないのです。また、クラスで `@CompileStatic` を使うことをおすすめします。もし使わなかった場合、Groovy の **動的ランタイム** が使われることになります。モバイルアプリケーションにおいては好ましくないリフレクションが起こり、パフォーマンスに影響を及ぼすかもしれません。通常の JVM では、Groovy はランタイムや invokedynamic でのクラスの生成などのコールサイトキャッシュのテクニックが使われますが、Android 上ではこれは **不可能** です。ですから、Groovy の動的な機能をアプリケーション内で使うのは、部分的にとどめておくべきで、頻繁に呼び出すべきではありません。この良い例が UI の Builder です。 Builder は UI 向きの Groovy における非常に良い機能で、activity が呼び出されたときたった一度だけロードされます。

`@CompileStatic` を使うと、 **静的コンパイル** が実行され、これはコンパイル時にメソッド呼び出しが解決され、Java と同じではないにしろ、同じくらいにパフォーマンスが改善されます（これはどにのようにコードを書くかによって異なります。理想的な Groovy コードですら最速の実装というわけではありません）

いずれにせよ、私が書いたアプリケーションにおいてランタイムで生成されたクラスがひとつも使われていないことを思い出して下さい。もしすべての `@CompileStatic` アノテーションをすべて取り除いたとしても、動的ランタイムは使われますが、ランタイムでのクラス生成は行われ **ません** 。

### ラインタイムスクリプティング

Groovy の適用にあたって、なにが難しかったかといえば、Groovy が高度に動的な言語であるといことです。そのひとつの特徴は、ランタイムでのスクリプティングです。デバイス上で Groovy のスクリプトを書いて、直接実行したいですか？　それは可能なのでしょうか？　事実、可能です。次のようなプロセスです。

![Groovy on Android](http://melix.github.io/blog/2014/06/compilation_process_runtime.png)

Groovy で書かれたアプリケーションであるかそうでないにせよ、 **一度アプリケーションを実行したあとで** 実行したい Groovy のコードがあるとします。そういう場合、 **デバイス上で** 直接 `dex` を呼び出しクラスをコンパイルし、`jar` ファイルにし、特別なクラスローダーを使う必要があります。このプロセスはとても複雑ですし、直接的ではなく、そしてこれが重要なのことなのですが、とても遅いです。

[このアプリケーション](https://github.com/melix/grooidshell-example)でこのプロセスのデモを観ることが出来ます。よく知られた [GroovyShell](http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/#integ-groovyshell) の Android 上で直接的に動作するコピーです。もしなにかアイディアを思いついたとしたら、お手持ちのデバイスで（私の場合は Galaxy S3 で）まずはアプリケーションを立ち上げます、そして Groovy を書きます。そして "execute" ボタンを押します。小さいスクリプトでも初回ロード時には、3s くらいかかります。

![Runtime Scripting](http://melix.github.io/blog/2014/06/groovy-scripting.png)

サブシークエンスのコンパイルは、より短い時間（〜500ms 程度）で済みます。しかし、dex とファイルシステムへの書き込みはパフォーマンスキラーです。いずれにせよ、スクリプトを扱うアプリケーションを作ることができるということを示しています。た追えば `jar` ファイルのコンパイルを避けるためにキャッシュするなどの方法が考えられます。

## まとめ

この投稿では、Groovy コードがどのようにして Android 上で実行されるかについて詳細を述べました。次の投稿では、どのようにプロジェクトをはじめればよいのかについて詳細を書きたいと思います。[Lazybones](https://github.com/pledbrook/lazybones) やプロジェクトのブートストラップ用ツールを使うかもしれません。それでは数日後に。

*翻訳ここまで*

---

## 感想

WWDC 2014 以降、 IT エンジニア界隈の話題はもっぱら Swift ですが、 Android 上で Groovy が動くようになったのも、同程度画期的な事柄だと個人的には思います。といっても私はまだ Groovy は Jenkins のジョブ構築くらいにしか使ったことがないので、真価はわかっていないのでしょうけれど。個人的に Android アプリケーションを書く場合は、 Groovy をメインで使っていきたい所存です。

翻訳が不十分なところもあると思います。怪しいところは原文をあたっていただくようお願い致します。