こんにちは、direct の Android エンジニアをしている高良(@takara727)です。プライベートのブログも含めブログを書くのは数年ぶりで拙い記事ですが、お付き合い頂ければ幸いです。 Google I/O 2017 で Android の開発言語として Kotlin が正式に採用されることが発表されてからというもの、開発に Kotlin を採用しているプロジェクトは日増しに増えてきているように感じます。
10月末に Kotlin が正式にサポートされた Android Studio 3.0 がリリースされたので、弊社の direct も11月にUnitテストを Kotlin 化、今年に入ってから本体にも Kotlin を導入し始めました。
心配性な私には Kotlin を導入する前に懸念していたことが5つありました。
実際に導入してみてこの懸念がどうだったのか検証しようと思います。
====
抱えていた5つの懸念
- 移行作業にかかる時間
- 実行時エラーのデグレード
- Java コードとの運用性
- ビルド時間
- 実行速度
移行作業にかかる時間
Android Studio 3.0 から Java コードの Kotlin へのコンバート機能が追加されたので、Command+Shift+Option+K を押すだけで変換できます。
しかし、うまく変換できてない箇所が少なくない箇所あり、コンパイルエラーになってしまうので手直しが必要です。
幾つか代表的なコンパイルエラーを挙げてみます。
- Nullable なフィールドへ !! や ? なしでアクセスしてしまっておりエラー
- Kotlin では raw type が使えないので raw type の利用箇所がエラー
- package private なメンバーの変換後のアクセス修飾子が internal になっており apt で自動生成されたJavaクラスから参照できないのでエラー(Daggerの利用箇所等でよく起きた)
- inner/nested クラスの private なメソッドを enclosing クラスから呼び出せなくなったのでエラー
- static メソッドや定数が companion object のメソッドへうまく変換されておらずエラー
一度でプロジェクトのコードを Kotlin に変換し、すっきりできたらいいなとプロジェクトを全て変換してみたのですが、1日手直しをして direct のコード規模だと数週間は必要だとわかったので途中で取りやめました。
今は機能追加やバグ修正による変更箇所に限って Kotlin にしていく戦略でことを進めています。
実行時エラーのデグレード
コンパイルエラーで移行に関わるデグレードがすべて検出できたらいいなーと思っていましたが、やはりそうは問屋がおろしません。
Nullable な変数を Non-Null に変換してしまう箇所があり、Java から呼び出されているコードの場合はコンパイルエラーにならず実行時にエラーになるので、Kotlin に変換してしまった箇所はテストが必須です。
機能追加やバグ修正による変更箇所に限って Kotlin にしていく戦略はここでも役に立ちました(そういった変更箇所はどちらにせよテストを通すので)。
代表的なものに LoaderManager.LoaderCallbacks#onCreateLoader
の第二引数である Bundle
の Non-Null 化があり、Loader
を初期化する際にエラーが発生していました。
Java コードとの相互運用性
apt で自動生成された Java コードとの連携や Kotlin 化が終わっていないクラスとの連携が必要なので Java コードとの相互運用は必須です。
そもそも Kotlin が Java コードとの相互運用性を念頭において設計されているので本来そこまで心配する必要はないのですが、某altJSでJSとの相互運用性に苦しんだ記憶がある身としては「はい、そうですか」と信じるわけにはいきません。
しかし試してみたところ良い意味で期待を裏切ってくれて、とても楽に運用できます。
Kotlin のコードから Java のコードを呼び出しは自然に出来ましたし、Java から Kotlin のコードを呼び出す際も困った際に公式ドキュメントのCalling Kotlin from Java に目を通せば解決できました。
ビルド時間
Swift にしたらビルド時間が劇的に伸びたという話を耳にしたりしていたので、Kotlin でも同じことが起きるのではないかという不安がありました。 そこで実際に計測してみたのが次の表です。 毎回 clean を実行した後にビルドして計測しています。
1回目 | 2回目 | 3回目 | |
---|---|---|---|
Kotlin 導入前 | 3m 16s 485ms | 2m 52s 780ms | 2m 55s 188ms |
Kotlin 導入後 | 3m 28s 220ms | 3m 23s 885ms | 3m 8s 280ms |
この表を見てわかるように数十秒ビルド時間が伸びてしまうようですが、せいぜい10%程度の違いのようなのであまり問題にはならない気がします。 ただし、この計測はまだまだ Kotlin 化したファイルが少ない状態で行っていますので、後々増える可能性があるので注意してください。
追記
ビルド時間測定時点での Kotlin 比率を質問されました。 ビルド時間測定時点で Kotlin 比率は10%弱です。 Kotlin 比率が増えるにつれビルド時間が着実に伸びている感じはあるので、Kotlin 比率が100%近くになったら改めて計測した結果を記載しようと思います。
実行速度
direct で表示が一番遅くなりがちなトーク画面の表示を Kotlin に変更して試してみましたが、体感できる速度の落ち方はしていないようです。 アクションゲームなどのようにセンシティブなアプリでない限り問題になることは少なそうです。
おまけ
メソッド数
Android は 64k method 問題があるのでメソッド数の増加は気になるところだと思います。 ただ direct は Kotlin 導入前から Proguard によるコードの圧縮前は 64k を超えていました。 そのため開発時は multidex、リリース時は Proguard によるコードの圧縮をかける運用をしていたため、Kotlin 導入によりメソッド数が増えてもあまり影響はないので、メソッド数の増加についてはあまり懸念していませんでした。 しかしそうは行かないプロジェクトは多々あるかと思います。 そこでせっかくなので Kotlin 化によるメソッド数の増加も計測しておきました。参考にしてください。
メソッド数 | フィールド数 | |
---|---|---|
Kotlin 導入前 Proguard なし | 91415 | 64201 |
Kotlin 導入前 Proguard あり | 57774 | 32945 |
Kotlin 導入後 Proguard なし | 97767 | 64990 |
Kotlin 導入後 Proguard あり | 57865 | 32968 |
まとめ
ここまで試した限り、Kotlin 移行前に抱えていた懸念は特に大きな問題にはなりませんでした。 RxJava、MVVM と最近取り込んできた中でも、一番楽に導入でき熟練しなくても効果的に使えるのがこの Kotlin かなと感じています。 Kotlin 導入に尻込みしていたがこの記事を読んで試してみようと思ったというかたが一人でもいたら幸いです。
社員を絶賛募集中
L is B では新しいことに挑戦したいエンジニアを募集しています。 こちらのURL よりご応募いただき、その際に高良(@takara727)の記事を読んだとおっしゃっていただけると(高良の)評価があがるのでどしどしご応募ください。