雰囲気で乗り切っているひとのためのAndroidビルド高速化ノウハウ
mhidakaです。久しぶりにAndroidアプリ開発の技術記事です。タイトルの「雰囲気で乗り切っているひとのためのAndroidビルド高速化ノウハウ」は書いている間にどんどん長くなってしまってGradleビルドシステム、CI/CD、ビルド時間等の計測手法という3つの内容をカバーするものになりました。
今回はそのなかから抜粋して、1つめのGradleビルドシステムを取り上げます。Androidアプリのビルドが早くなる(なった)Gradle設定を紹介します。プロジェクトルートにあるGradleビルドプロパティ設定値を変えるだけなので開発プロジェクトでも試しやすいと思います。いけそうなら使ってみてください(既に導入してたら開発プロジェクトの感度が高い…!雰囲気は卒業できています!)。
Gradleビルドキャッシュを使おう
Gradle 7.1.x以上 / Android Studio Bamblebee Canary 13以上を推奨
Gradle 7.0 / Arctic Foxでも効果があります
プロジェクトルートにあるgradle.propertiesのビルドプロパティ設定値にorg.gradle.caching=trueを使ってみてください。Gradleタスクで何度もビルドしている工程があればキャッシュを使うのでビルドを早く終われます。
org.gradle.caching=true
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC
4つほどパラメータ紹介していますがorg.gradle.caching=true以外はおまけです。使う際は無しでもいいと思うので検証しつつ追加してみてください。それぞれ意味は次のとおりです。
caching: Gradleビルドキャッシュを利用する
configureondemand: 実験的機能。タスクの依存関係にあるプロジェクトのみを実行するのでマルチプロジェクトで効果的
parallel: 並列数をorg.gradle.workers.max(デフォルトはCPU数)に指定
jvmargs: ビルドJVMの起動パラメータ(Xmxはローカル/CIに合わせて)
Gradleビルドキャッシュの詳細はリファレンスを確認してみてください。
ベンチマーク結果
Gradleビルドキャッシュはキャッシュという仕組み上、2回目以降のビルドで最も効果があります。上記のパラメータをDroidKaigi公式フィードアプリに設定し、1回目と2回目のビルド速度を比べてみます。
./gradlew clean --profile --offline assembleDebug
効果が得られやすいcleanビルドでの差ですが、2回目以降のビルドはDroidKaigi公式フィードアプリで、おおむね3倍程度、筆者(mhidaka)が色々調べたり確認した結果、プロダクトコードでは10倍程度、ビルド待ち時間を短縮でしました。これはプロダクトが複雑な構造を取りやすいことが影響しています。
実際に早くなるかは開発スタイル次第だけど便利
実際の開発シーンでは毎回cleanビルドはしないので、上のベンチマークはもっとも効果がある場合という前提での計測です。じゃあ実務だと実感しないの?ということで自身でもしばらくキャッシュを有効にして実開発してみました。筆者の関わるプロジェクトたちの場合はIncrementalビルドでもGradleビルドキャッシュの効果を感じられました。キャッシュをどの程度使っているかはビルド時の出力に from cacheの文字列があるかどうかでわかります。
BUILD SUCCESSFUL in 9s
367 actionable tasks: 109 executed, 207 from cache, 51 up-to-date
キャッシュは大規模なプロジェクトほど効果があります。1回目のビルドであっても思いの外、早く感じます。1度ビルドしてしまえばブランチを切り替えても引き継いだビルドキャッシュがうまく動くのでPRをいったりきたりも大分楽になりました。普段のアプリ開発ではソースコードの大半は変化していないので使わない手はなさそうです。
Android Lintも早くなる
最初に推奨環境を「Gradle 7.1.x以上 / Android Studio Bamblebee Canary 13」と記載しましたがこれには理由があります。Android Lintタスクがこのバージョン以降、Gradleビルドキャッシュに対応したからです。
Lint cachingはCIサービスで使うと嬉しい機能です。ビルド後のLinter待ちも結構ストレスになりやすいのでぜひ使ってみてください。Android Lintは大変重い処理で、CI/CDでは利用インスタンスサイズの影響が大きくなります。自分たちにとって最適なサイズを色々試してみてください。
キャッシュが効かないケースに遭遇したら
数週間つかってみてたまにキャッシュが悪さしてるな、という瞬間がありました。Gradleタスクのキャッシュ(これはAndroid Gradle Pluginが頑張っています)なのでコードに差がないと判断したタイミング等によっては再ビルドをSkipしてしまい、、、というときがあります。
ビルドコマンドのパラメータ等で変数を渡す、エンドポイントを渡すなどが開発者の意図しないキャッシュ利用に陥りやすいようです(CLI上でビルドのための付加情報を渡すとAGPからは、うまくコード生成を検出できてない可能性があるっぽい)。
Gradleビルドキャッシュを消す場合
rm -rf ~/.gradle/caches/build-cache-1
キャッシュを使わないで、とりあえずビルドする場合 --no-build-cache を使う
./gradlew clean assembleDebug --no-build-cache
そういうときはキャッシュを消すか、キャッシュを使わないビルドを試してみてください。
執筆時点のGradleはorg.gradle.caching=falseがデフォルト値です。誤解したりトラブルにならないようにということだと思いますが、理解した上でつかえば開発を高速化できるめちゃくちゃ便利な機能です。
[PR] もっと詳しい背景はこちらの書籍で
Gradleビルドシステムは、アプリ設計やKotlinプログラミング技法とはちょっと違う、でも開発の隣にあるビルドのための機構です。雰囲気で乗り切っていけるに越したことはないのですが便利なビルドパラメータを覚えながら、ちょっとずつ乗り越えていけると、新しいノウハウを吸収できてよさそう!ということで色々しらべた結果をまとめました。
アンドロイド・アンサンブル(B5/92P)発売中です
12月31日のコミックマーケット99に向けて書いた技術書「アンドロイド・アンサンブル」でGradleビルドシステムのトピックや解説、アプリビルドのボトルネックやチューニング方法、CI/CDでの注意点などをまとめています。
mhidakaが一番効果を実感してビビったGradleビルドキャッシュは本記事で解説したので満足です!「雰囲気をもっと脱してぇ」っていうひとや著者に金を払いたい~というひとは買ってみてください。いまなら電子版がすぐ読めます(内容の訂正や質問もお待ちしてます)。
もしビルドが早くなったらこの記事のコメントとかでも教えてください Have a nice build! :-)
この記事が気に入ったらサポートをしてみませんか?