Now in REALITY Tech #10 Android Gradle pluginをアップデートした話
REALITYでAndroidエンジニアをしているメタルおじさんです。3周年で配られたガチャチケを1枚使ったら浴衣がでました。今年の運勢は使い切りました。
Android Studio Arctic Fox 2020.3.1がStableになり、合わせてAndroid Gradle plugin 7.0.0が公開されました。REALITYでも早速このバージョンに対応しようと思って作業を開始したのですが、運勢を使い切ったためかいろいろと躓きポイントがあったのでまとめました。
Android Gradle pluginを更新するためにやったことは以下のとおりです。
1. build.gradleでcom.android.tools.build:gradle:7.0.0を使うようにする
2. ローカルマシンのgradle.propertiesで余計な設定をしていたのを外した
3. ローカルaarの取り込み方を変えた
4. CIのJavaバージョンをJava 11に更新した
5. ktorのためのproguard設定を追加した
細かく見ていきましょう。
(なお、アップデート前のバージョンは4.2.0でした)
Android Gradle pluginのバージョンを上げる
まず、プロジェクトルートにあるbuild.gradleでcom.android.tools.build:gradle:7.0.0を使うようにします。
buildscript {
repositories {
google()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.0" // ここを7.0.0にする
}
}
これだけでビルドがすんなり通れば良のですが…だいたい何かしらの問題が出ます。一発で上手く行ったら逆に不安になります。
gradle.propertiesの設定でエラー
まず、いきなり大量のエラーが出るようになりました。
Could not determine the dependencies of task ':app:compileDebugJavaWithJavac'.
> Could not resolve all task dependencies for configuration ':app:debugCompileClasspath'.
> Could not resolve project :features:chat.
Required by:
project :app
> No matching configuration of project :features:chat was found. The consumer was configured to find an API of a component, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug', attribute 'dimension1' with value 'develop', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
- Configuration 'ktlintRuleset':
- Other compatible attributes:
- Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')
- Doesn't say anything about its target Java environment (preferred optimized for Android)
- Doesn't say anything about its usage (required an API)
- Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'androidJvm')
> Could not resolve project :features:live.
... 以下、同文の繰り返し
buildFlavorの設定がおかしくなった?書き方が変わった?
…といろいろ原因を推測して調べて回ったのですが直らず。半日くらいの時間を溶かしました。
結論から言いますとbuildFlavorとか全然関係なくて、原因は自分のマシンでビルドを高速化するために(高速化しそうな気がして)Configuration on demandを有効にしていたからでした。
org.gradle.configureondemand=true
これはプロジェクトのgradle.propertiesではなく自分の開発マシンにのみ設定していたため、他の人のビルドやCIのビルドには影響していませんでした。
ちなみに、Gradleは $HOME/.gradle/gradle.properties を作って設定を書くとプロジェクトのgradle.propertiesの内容に加えてその環境専用の追加の設定を適用できます。私は少しでもビルドを速くしたいと思って、ブログで見つけた高速化のためのGradle設定をいくつか追加していたのですが、それが良くなかった…よくみたらこの機能は実験的機能であるという注意書きがビルド時にログに出るし、ドキュメントにも
・非常に大規模なプロジェクト向きの機能
・全てのビルドが正しく動作するとは限らない
ということが記載されていました。きちんと調べず上辺だけの知識でテキトーな設定をしていると痛い目に合うんですね。皆さんも気をつけましょう。
ローカルaarの取り込みでエラー
次はローカルのaarファイルへの依存を直接宣言している部分でエラーが出るようになりました。昔のAndroid Studioではbuild.gradleのimplementationでローカルのaarファイルを直接指定する方法が使えず、別モジュールを作って以下のように記述していました。
// aar-module/build.gradle
configurations.maybeCreate("default")
artifacts.add("default", file('hoge.aar')) // aar-module/hoge.aar
// aar-moduleを使う側のbuild.gradle
dependencies {
implementation project(':aar-module')
}
Android Studioでaarファイルをインポートするときもこのようなモジュールが作られていました。
Android Gradle plugin 7.0.0に上げたブランチではこのビルドスクリプトが動かなくなっていました。しかも、よく見るとAndroid Studio上のモジュールインポート機能からもこのメニューがなくなっているではないですか…
aarファイルのインポート方法を調べてみたところ、implementation files()で直接aarを指定する方法にしてみたらAndroid Studio上でのビルドは動くようになりました。
// hoge.aarを使いたいモジュール
dependencies {
implementation files('libs/hoge.aar')
}
これで解決!
…と思ったのはつかの間、今度はCI上のテストがコケるようになりました…
Direct local .aar file dependencies are not supported when building an AARというメッセージが出ていました。aarに依存するAndroidライブラリはサポートしていないとのことです(以前のAndroid Gradle pluginではビルドは通るもののaarの出力が正しくなかったようで、エラーが出るようになるのが本来正常な挙動だったみたい)。
implementation files('libs/hoge.aar')は、apkをビルドするモジュールに書くのは良いが、aarをビルドするモジュールに書いてはいけないということがわかりました。
最終的に、以下のようにすることで回避できました。
// hoge.aarを使いたいモジュール(:aar-module)
dependencies {
compileOnly files('libs/hoge.aar')
}
// ↑のモジュールを使い、apkをビルドするモジュール
dependencies {
implementation project(':aar-module')
implementation files('libs/hoge.aar')
}
ローカルにhoge.aarがあり、これに依存しているaarモジュール(aar-module)のbuild.gradleにはcompileOnlyでhoge.aarへの依存を宣言します。そして、aarモジュールに依存するアプリモジュール側のbuild.gradleにはaar-moduleへの依存と一緒にhoge.aarへの依存も宣言します。これでDirect local .aar file dependencies are not supported when building an AARのエラーが回避できました。
本来使いたいモジュールが内部で使っている推移依存のライブラリについてアプリ側に記述するのはちょっと気持ち悪い感じがしますが…とりあえずビルドエラーは回避することができました。が、マルチモジュールのプロジェクトではローカルaarを使うのはなるべく避けたほうが無難かもしれません。
CIのJavaバージョンが古い
ここまでやってローカルのAndroid Studioでのビルドはできるようになったのですが、今度はBitrise上のビルドジョブがコケるようになりました。
これはBitriseがデフォルトでJava 8の環境でビルドを実行していたからで、Java 11の環境でビルドを行うように設定を変更したらビルドが動くようになりました。
やり方はシンプルで、
1. Android Buildより手前にスクリプト実行のステップを追加する
2. Java 11をインストールしてJAVA_HOMEとして使用するための設定を書く
これだけでした。具体的な設定内容はビルドに使っているインスタンスの種類によっても異なると思うので、公式のドキュメントを御覧ください。
※BitriseのデフォルトのJavaバージョンが2021年9月中旬頃にJava 11に変更されるらしいので、この手順は不要になると思います。
ktorのWebSocket接続ができない
ローカルのビルド・CIのビルドが共に動くようになり、Android Gradle plugin 7.0.0の対応が完了した!そろそろ今週のビルドをストアにアップロードするか〜
…と思っていた矢先に、releaseビルドだと配信音声の送信ができないという不具合報告がチーム内から寄せられました(ストアにアップロードする前に気付けて本当によかった…)。
WebSocket処理のあたりにログをいっぱい仕込んでどこの処理で止まっているのかを調べてみたところ、WebSocketの接続を行う処理まで行った後に成功も失敗もしていないことがわかりました。
val client = HttpClient(OkHttp) {
install(WebSockets)
}
try {
client.ws(endpointUrl) {
// ここには来てないし
}
} catch (e: Exception) {
// ここにも来ない
}
※REALITYでは配信者やコラボ参加者が音声を送受信するのにWebSocketを使っており、WebSocketクライアントの実装にktorを使っています(最近使い始めました)。上記はktorでWebSocket接続を行っている箇所の処理を説明用に抜き出したものです。
Android Gradle plugin 7.0.0に上げる前は無事でしたし、Android Gradle plugin 7.0.0に上げてからもdebugビルドでは普通に動いていました。releaseビルドでだけ行っている最適化が影響しているのだと思います。
しかし、REALITYとは別に新規プロジェクトを作成してktorのWebSocket接続処理を書いてreleaseビルドの最適化をかけてみましたが、正常に動いてしまいました。なぜREALITYでだけ動かないのか?何か別の要因があるのか?というところまでは結局調べきれませんでした…
謎が完全に解けていなくてモヤモヤしますが、とりあえずREALITYのアプリモジュールでproguard-rules.proに以下の設定を追記してktor関係のクラスを保持するようにしたら、直りました。
-keep class io.ktor.** { *; }
これで、本当にAndroid Gradle plugin 7.0.0へのアップデートが完了しました!
最後に
ビルド環境の更新は意外と面倒事が多くてついつい後回しにしてしまいがちですが、放ったらかしにしておくとその面倒さはどんどん大きくなり、やがて
・リポジトリをクローンしてきたけどビルドできない
・特定のバージョンの開発ツールをインストールしないとビルドできない
・OSをアップデートしたらビルドできなくなった
等の問題に発展します。私自身、以前いたプロジェクトでそういった経験を何度もしました。
なので、REALITYはそうあって欲しくないと思っています。
共感していただけるAndroidエンジニアの方からのご応募をお待ちしております。
カジュアル面談も受け付けているので、お気軽にお申し込みください!
Android以外の技術領域が気になる方はこちら