🎉

ココナラAndroidアプリのDIライブラリをDaggerからHiltに移行してみた

2024/04/17に公開

株式会社ココナラアプリ開発グループ、Androidチームの長谷山です。

今回はココナラAndroidアプリのDIライブラリをDaggerからHiltに移行したことをご紹介します。

背景

ココナラのAndroidアプリは2018年にDaggerの導入を行いました。
Daggerを使用することにより、DI(Dependency Injection)を簡単に行うことができます。
ただ、Daggerはセットアップが大変だったり、学習コストが高かったり、Androidで対応するのが難しかったりという問題点があります。

そこで問題点を解決するために、Daggerの上に構築されたHiltライブラリが開発されました。
今ではHiltがAndroidでDIを行うために推奨されるライブラリとなっています。
https://developer.android.com/training/dependency-injection/hilt-android?hl=ja#hilt-and-dagger

今回Hiltに移行しようとしたのは、下記メリットを享受するためです。

  • Google公式の各種サンプル実装がHiltを使用している
  • 他のJetpackライブラリのサポートが含まれている
  • Androidコンポーネントとの統合時に生まれる大量のボイラープレートが不要となる
  • AndroidでDIを実装したことがない、新卒の学習コストを抑える

ライブラリの導入

HiltのバージョンはKotlinのバージョンに依存しています。
ココナラではKotlin 1.7.0を使用しているため、Hiltのバージョンは2.45を使用することになりました。
ちなみに、Hiltの2.45以上のバージョンを使用すると

error: [Hilt] Unsupported metadata version. Check that your Kotlin version is >= 1.0: java.lang.IllegalStateException: Unsupported metadata version. Check that your Kotlin version is >= 1.0

というエラーが発生します。
Hilt 2.46でkotlinメタデータが0.6.0に更新されているためです。

また、Compose、WorkManagerを使用しているため合わせて依存関係に追加します。

build.gradle(プロジェクト)

buildscript {
    ..
    dependencies {
        ..
        classpath("com.google.dagger:hilt-android-gradle-plugin:2.42")
    }
}

build.gradle(app)

plugins {
    ..
    id("kotlin-kapt")
    id("dagger.hilt.android.plugin")
}

android {
    ..
    dependencies {
        ..
        implementation("com.google.dagger:hilt-android:2.45")
        implementation("androidx.hilt:hilt-navigation-compose:1.1.0-alpha01")
        implementation("androidx.hilt:hilt-work:1.0.0")
        kapt("com.google.dagger:hilt-android-compiler:2.45")
    }
}

Hiltに移行してみる

DI部分の移行になるため、問題が発生する場合は該当の画面に遷移したときとなります。
そのため、まずは移行するにあたり影響が発生する箇所の洗い出しを行いました。
実装後のテスト、また実装漏れのチェック用に役に立ったので、もし移行を検討しているならおすすめです!

DaggerからHiltへの移行は、「DaggerアプリをHiltに移行する」コードラボを見ながら対応しました。

https://codelabs.developers.google.com/codelabs/android-dagger-to-hilt

かなり丁寧に記載されているため、参考にしながら作業を行えば問題なく移行することができると思います。
ココナラアプリはマルチモジュールになっているため、モジュール単位で区切って対応を行いました。

今回の対応では、全てをHiltに移行してからリリースを行いましたが、DaggerとHiltの共存が可能なため都度リリースを行うことも可能です。

つまったところ

基本的には問題なく移行ができたのですが、テスト時にクラッシュが発生する箇所がありました。

 val activity = context as Activity

このcontextはFragment.getContext()で取得したもので、Hilt移行前には問題がなかったcast部分です。
エラーを見てみると、

java.lang.ClassCastException: dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper cannot be cast to android.app.Activity

dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapperとなっています。
調べてみたところ、どうやら@AndroidEntryPointをFragmentにつけるとcontextがラップされてしまうようです。

解決方法として、stack overflowでは、dagger.hilt.android.internal.managers.FragmentComponentManager.findActivityを使用すると記載があります。
...が、ちょっと待った!

コードを見てみると

  /**
   * Do not use except in Hilt generated code!
   *
   * <p>A wrapper class to expose the {@link Fragment} to the views they're inflating.
   */

のコメントが。
Hiltで生成されたコード以外に使わないでね、とのこと。

ココナラでは該当箇所が1つしかなかったため、素直にActvityを使用するように修正しましたが、もし同じようなクラッシュが発生した場合は対応方法を考える必要がありそうです。

結果

Hilt移行はスケジュール通り実装が完了し、リリース後クラッシュ等の問題も発生せずほっとしています。

移行した結果、当初の目的の1つだった学習コストを抑えるという点を達成できました。

勉強会で30分ほどAndroidチームメンバーに共有の時間を設けましたが、
2023年新卒のメンバーもHiltについて理解をしてくれました。
Daggerのままだと、さらに何倍もの学習時間が必要になったはずです。

また、もう1つの目的だったボイラープレートの削減についても達成することができました。

移行前後の差分になります。
Hilt差分
いかがでしょうか?
このくらいコードが減ると、ちょっとテンションあがりますよね!

まとめ

今回はココナラAndroidアプリのDIライブラリをDaggerからHiltに移行した内容をお伝えしました。
Hilt移行に迷っているなら、ぜひやってみてください!

現在、Android14対応中なのですが、こちらではAGPのバージョンアップも必要になってしまいました。

何かをするときに芋づる式に他も対応しなくちゃいけないという状況って結構発生しますよね。
そうならないよう、やってみたいと思ったことがすぐできる環境を作っていきたいと思います!

ココナラでは、一緒に事業のグロースを推進していただける様々な領域のエンジニアを募集しています。
アプリ開発だけでなく、フロントエンド領域・バックエンド領域などでも積極的にエンジニア採用を行っています。
少しでも興味を持たれた方がいましたら、エンジニア採用ページをご覧ください。

https://open.talentio.com/r/1/c/coconala/pages/70417

https://coconala.co.jp/recruit/engineer/

Discussion