google-code-prettify

ラベル gradle の投稿を表示しています。 すべての投稿を表示
ラベル gradle の投稿を表示しています。 すべての投稿を表示

2014-05-05

dx.jarをfastdx.jarに入れ替えて、Androidのビルドを50%くらい速くする

Androidのアプリのビルドは非常に遅い。原因の一つはGradleが重いことなのだが、実はDEXとPRE-DEXのフェーズも無駄な処理を行なっているため、非常に時間がかかる。
そこで、DEXとPRE-DEXを行なっているdx.jarの高速版のfastdx.jarを作成した。全体のビルド時間は、私の環境では50%くらい高速化された。

fastdx.jarのダウンロードはこちら

オリジナルのdx.jarが遅い原因は、大きく2つある。

  1. 無駄に.classから.dexの作成を行なっている(キャッシュしていない)
  2. 複数のdexのマージの処理に無駄がある

無駄に.classから.dexの作成を行なっている(キャッシュしていない)


オリジナルのdx.jarでは、.jarに含まれていた.classが一つでも変更された場合、.jarに含まれていた全.classファイルを再度.dexにコンバートし直している。
例えば、.jarの中身が以下の3つの.classを含んでいたとする。

a.class
b.class
c.class

a.classが変更された場合、dx.jarはa.dexのみならず、b.dex, c.dexも再度生成する。このコンバートに結構時間がかかる。

fastdx.jarでは、コンバート済みの.dexをキャッシュすることで、無駄に.dexへのコンバートが発生しないようにした。

複数のdexのマージの処理に無駄がある


複数のdexをマージする際、例えばa.dexからd.dexまでをマージする際、オリジナルのdx.jarは以下のようにdexをひとつずつマージする。
a.dex + b.dex = ab.dex
ab.dex + c.dex = abc.dex
abc.dex + d.dex = abdc.dex
dexファイルをいちいち生成、再読み込み、を行うため、非常に時間がかかる。

fastdx.jarでは、マージを一気に行う。
a.dex + b.dex + c.dex + d.dex = abcd.dex

このため無駄なdexの生成と読み込みが無くなり、高速化されている。



2014-04-24

AndroidのGradleでのビルドが遅いのをちょっと速くする

Gradleを使ってAndroidアプリのビルドをしていると、あまりのビルドの遅さについついGradleに移行したことを後悔してしまいそうになる。
遅い原因を調べて、2つほどビルドを速くするポイントを発見した。
  • 実はGradleデーモンが使われていない問題
  • 実はlibrary projectはReleaseとDebugの両方がビルドされている問題
以下、IntelliJ 13.1.2での症状&対処法だが、Android Studioでも同じ方法で速くなると思われる

前者は、Android Studio 0.6.0で直ったようです。


実はGradleデーモンが使われていない問題


.gradle/gradle.propertiesに
org.gradle.daemon=true
と書いておけばGradleデーモンが使われるので立ち上がりが速くなる、というのはあちこちに書いてあるが、実はGradleデーモンが使われていない可能性がある。

その理由は2つ。
  • IntelliJはGradleデーモンがいない場合にはGradleデーモンを起動するが、アイドルタイムアウトが60秒(固定値)。そのため、次にビルドするときにはGradleデーモンが既に死んでいる。
  • 手動でGradleデーモンを立ち上げたとしても、IntelliJが起動しようとするGradleとあまりに引数が違うと、新たに別のGradleデーモンが(timeout60秒で)起動する。
Gradleデーモンの引数やアイドルタイムアウトは、ps all wwで確認できる。
0  1000 10170     1  20   0 2124512 1254752 futex_ Sl pts/3    11:15 /usr/lib/jvm/java-6-oracle/bin/java -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError -Xmx1024m -Dfile.encoding=UTF-8 -cp /home/sakurai/.gradle/wrapper/dists/gradle-1.11-all/7qd8qq8te5j4f5q9aaei3gh3lj/gradle-1.11/lib/gradle-launcher-1.11.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 1.11 /home/sakurai/.gradle/daemon 10800000 7e2c5ad5-a731-455d-a37b-699b5c251ea6 -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError -Xmx1024m -Dfile.encoding=UTF-8
IntelliJでのビルド直後にps all wwで、timeoutの値をチェックしてみると良い。数値が60000の場合には、60秒で死ぬデーモンが使われている。タイムアウトが長いデーモンを使うには、手動でGradleを立ち上げるしか無い。

また、Gradleの引数が一致していない場合には、IntelliJのSettings->Gradle->Gradle VM Optionsの値を、手動で起動したGradleと合わせておく必要がある。私の場合には、以下を設定してある。
-XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError -Xmx1024m

実はlibrary projectはReleaseとDebugの両方がビルドされている問題


Build VariantsをDebugにしていると、library projectはReleaseとDebugの両方がビルドされている(library以外は関係ない)。

現状、Debugビルドをしていても、必ずReleaseビルドのlibraryが使われてしまうというバグがある。
Gradle plugin does not propagate debug/release to dependencies

このバグの副作用として、アプリをDebugビルドした場合に、library projectはReleaseとDebugの両方がビルドされてしまうようだ。

Gradleの出力例: (DebugもReleaseもビルドされている)
:ActionBarSherlock:preReleaseBuild
:ActionBarSherlock:checkReleaseManifest
...
:ActionBarSherlock:preBuild
:ActionBarSherlock:preDebugBuild

どうせReleaseビルドしか使われないのだから、最初からlibraryはReleaseビルドしかしないようにしてしまえばよい。

IntelliJのウィンドウの左下のBuild Variantをクリックし、libraryプロジェクトのBuild Variantは全てReleaseに変えておく。