Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

スタディプラス AndroidアプリKotlin化の歩み

こんにちは、Androidチームの若宮(id:D_R_1009)です。 今回はAndroidアプリの大きな更新、JavaからKotlinへの移行について書きたいと思います。

Androidアプリの歴史

Kotlinの導入

スタディプラスのAndroidアプリは2016年1月ごろにフルリニューアルを行い、そのまま開発を続けています。 Kotlinは2017年12月ごろの導入となるため、コードの大半はJavaで記述されています。

その後、2018年3月ごろから本格的にKotlinへの移行(コードのKotlin化)を進め、8月ごろには20%を占める程度になりました。

tech.studyplus.co.jp

Kotlin化の本格化

2018年9月よりフルタイムの開発者が2名に、2019年5月より3名になりました。 また副業でkobakeiさん(id:keisukekobayashi)に入ってもらったことにより、Kotlin化が本格化します。

以下、大きな変更や方針が決まった時期を振り返ってみました。 もちろん、合間合間にリファクタリングやマルチモジュール化に伴うコードの整理が行われています。

  • タイムラインデザインリニューアル(2019年10月)
  • Kotlin Coroutines導入、RxJavaから移行開始(2018年11月)
  • デザインリニューアル(2018年11月)
  • マルチモジュール構成へ移行開始(2018年12月)
  • 友達からフォロー/フォロワーへの更新に伴うアプリの一新(2019年3月)
  • 大学情報関連画面のリファクタリング(2019年3月)
  • 内部DBにRoomのDatabaseViewを導入(2019年5月)
  • AndroidXへの移行(2019年6月)
  • NavigationによるFragment遷移実装開始(2019年7月)
  • アカウント作成方法更新(2019年8月)
  • ネットワークレスポンス用DataクラスのKotlin化 (2019年9月)

結果として、2019年9月末を持ってKotlinが全体の86%を占める状況となりました!

f:id:D_R_1009:20190930181835p:plain

ここに至るまでに大きな影響を与えた出来事について、いくつか抜き出してみたいと思います。

Kotlin Coroutines導入、RxJavaから移行開始(2018年11月)

Kotlin Coroutinesのstable版が2018年10月にリリースされ、AndroidチームではまずSDKに導入しました。 下記ブログを導入直後に書いたことを覚えています。

tech.studyplus.co.jp

SDKへの導入に続いて、スタディプラスへKotlin Coroutinesを導入しています。 RxJavaを利用していた箇所が多かっため、RxJavaをKotlin Coroutinesに置き換える処理が大半となりました。

RxJavaは下記のような目的で利用されていました。

  1. Single / Complete 型による通信
  2. RxBusによるクラス間連携
  3. Obserbable によるリスト操作

通信処理をOkHttp + Retrofitで行なっていたため、Kotlin化を簡単に進めることができました。 suspend を返り値とする対応はRetrofitのアップデートを待ってからとなりましたが、デフォルト引数の利用などだいぶコードの削減ができるようになりました。

2019年7月頃からRoom 2.1でKotlin Coroutinesがサポートされたため、Kotlinをより活用しやすくなっています。 またKotlin Coroutines 1.3.30からは Flow も導入されたため、 RxStream の処理も移行しやすくなりました。

Kotlin Coroutinesが登場したことにより、既存コードに +α を加えながらKotlin化しやすくなったと言えます。

マルチモジュール構成へ移行開始(2018年12月)

kobakeiさんには月1回勉強会を開いてもらっています。 その2018年11月のテーマが「マルチモジュール」でした。

当時のスタディプラスアプリはJavaとKotlinを合わせて10万行程度(Java 7.5万、Kotlin 2.5万)のシングルモジュールアプリでした。 設計はJavaのコードがActivityを中心としたMVC、KotlinのコードがAndroid Architecture Moduleを利用したMVVMが採用されていました。

当時開発していた時に上がっていた問題は、下記3点です。

  1. ビルド時間が長い
  2. 画面ごとに利用するメソッドがまとまっているため、処理がまとめられていない
  3. リファクタリング時に思わぬクラスへの影響が発生する

マルチモジュールに移行する際、一番期待していたのは「ビルド時間」の問題でした。 確かにマルチモジュール化により並列ビルドの恩恵を得られたのですが、同時にDaggerを導入したことにより相殺されてしまったのか、ビルド時間の短縮は感じられませんでした。

一方で、設計上は大きなメリットが得られました。 モジュール化を進める上で、まず entity (データクラス)モジュールから分離する必要があります。

スタディプラスアプリの場合、この entity モジュールの作成が難航しました。 というのも、データクラス内でネットワークインスタンスを呼び出すなどの処理をしている箇所が散見されたためです。 Kotlin化の早いタイミングで設計上の問題が見つかったため、結果として効率的にKotlin化を進めることができました。

一方で、クックパッドさんが行なっていた Legacy モジュールの対応は行えませんでした。 こちらは色々とモジュール移動に苦心することとなったため、行なっておけばよかったと強く後悔しています。

speakerdeck.com

マルチモジュール化と同時にアプリ全体を巻き込む機能開発(フォロー制への移行)が被ってしまったため、タイミングを逃してしまったことが大きかったように思います。 マルチモジュール対応を行う場合には、新規機能開発のタイミングと被らせずにスタートするのが良いのではないでしょうか。

1年間を通して見ると、kobakeiさんにマルチモジュール化の導入から実行までを強く推進してもらいました!

f:id:D_R_1009:20190930182212p:plain

マルチモジュール化によりKotlin化しやすくなる(クラス間の依存関係が一方方向になるため、影響範囲が限定される)ことを実感しています。 ビジネス的な成果は少ないのが少々難しいところですが、開発チームのタスクとして取り組むことを強くお勧めします。

ネットワークレスポンス用DataクラスのKotlin化 (2019年9月)

Kotlin Coroutines 1.3.30やOkHttp 4系を導入しようとしたところ、Proguardを起因とするビルドクラッシュが発生しました。 このため、ProguardからR8へ移行した方が良い状況となりました。

github.com

github.com

しかし、R8でGsonを利用すると問題が生じやすくなります。 この問題に対応するため、1週間ほどかけて全ての通信用データクラスをKotlin Dataクラスに変換しGsonからmoshiへ移行する対応を行いました。

r8.googlesource.com

また逆説的ではありますが、OkHttpやRetrofitなどのライブラリ側でKotlinが利用されるようになってしまったため、Kotlinの対応を見越した開発体制にする必要が生じています。 例えばKotlin CoroutinesのMainDispatchersの初期化遅延問題は、最新のR8(記事執筆時点でalpha版であるAGP 3.6以上)でなければ解決しません。

github.com

こういった大規模な問題が発生するまでデータクラスの整理を後回しにしていたので、少々タスクが重い状況になってしまいました。 Kotlin化を進める中で、少しずつサーバーチームと連携しながら進めていくのが良いように思います。

また知見としては、通信用のデータクラスのKotlin化をすることで下記のような事象に出くわしました。

  1. 古くからあるAPIのため特に理由もなくnullableとして扱っているプロパティが見つかった
  2. デフォルト引数により、non-nullな値として扱える箇所が複数見つかった
  3. Gsonではリフレクションにより継承関係を簡単に扱えたが、moshiでは継承関係をデータクラスの引数として表現する必要があった

2つ目は特にリストをプロパティとして持つJSONに有効でした。 これまでは orEmpty() を噛ませることで対応していた箇所が、デフォルト引数で emptyList() を指定するだけで対応が終わるようになります。 結果論ではありますが、型安全なコードを記述するためにも、Kotlin化は非常に有効な手段だと言えるのではないでしょうか。

終わりに

簡単ではありますが、スタディプラスアプリが1年ほどかけて60%ほど(削除しているコードを考えるとそれ以上! )をKotlin化した経験を振り返ってみました。 Kotlin化により、コードレビューの時に名前付き引数があるとレビューしやすいなど、様々なDXの向上を感じています。 機能開発の傍らであってもKotlin化を進めることを快諾してくれた企画部やCTO、並びにチームメンバー(中島さん、隅山さん)と副業エンジニア(kobakeiさん)にはいくら感謝しても感謝しきれません!

残り10%強のKotlin化、ならびによりユーザーにとってメリットのある設計を目指して、引き続き頑張っていきたいと思います。