Dagger Hilt の Context まわりでハマったこと

3 min読了の目安(約2800字TECH技術記事

Dagger Hilt では、Context クラスが必要な場合のために、@ApplicationContextアノテーションと@ActivityContextアノテーションが用意されています。
Hilt を使用した依存関係の注入  |  Android デベロッパー  |  Android Developers
Context を使う上でハマったポイントがあったので、備忘録として残しておきます。

事例

例えば SharedPreferences をラップした、SharedPreferencesWrapperクラスをMainActivityで使う場合を考えてみます。

MainActivity.kt
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var sharedPreferencesWrapper: SharedPreferencesWrapper
    ...
}
SharedPreferencesWrapper.kt
/**
 * SharedPreferencesのラッパークラス
 */
class SharedPreferencesWrapper @Inject constructor(@ActivityContext context: Context) {

    private val sharedPreferences = context.getSharedPreferences("test", Context.MODE_PRIVATE)
    ...
}

そして、SharedPreferencesWrapper のインスタンスを提供するため、ActivityProvidesModuleクラスを以下の通り作成します。

ActivityProvidesModule.kt
/**
 * DI用ProvidesModule
 * @Inject が付いたプロパティや引数に提供する値の実体を定義
 */
@Module
@InstallIn(ActivityComponent::class)
object ActivityProvidesModule {

    /**
     * SharedPreferencesWrapperの提供
     */
    @Provides
    fun provideSharedPreferencesWrapper(context: Context): SharedPreferencesWrapper {
        return SharedPreferencesWrapper(context)
    }
}

ここでアプリをビルドすると、以下のエラーが出力されます。

/Users/Hitoshi/src/MyApplication/app/build/generated/source/kapt/debug/com/kmdhtsh/myapplication/MainApplication_HiltComponents.java:144: エラー: [Dagger/MissingBinding] android.content.Context cannot be provided without an @Provides-annotated method.
  public abstract static class ApplicationC implements MainApplication_GeneratedInjector,
                         ^
      android.content.Context is injected at
          com.kmdhtsh.myapplication.ActivityProvidesModule.provideSharedPreferencesWrapper(context)
      com.kmdhtsh.myapplication.SharedPreferencesWrapper is injected at
          com.kmdhtsh.myapplication.MainActivity.sharedPreferencesWrapper
      com.kmdhtsh.myapplication.MainActivity is injected at
          com.kmdhtsh.myapplication.MainActivity_GeneratedInjector.injectMainActivity(com.kmdhtsh.myapplication.MainActivity) [com.kmdhtsh.myapplication.MainApplication_HiltComponents.ApplicationC → com.kmdhtsh.myapplication.MainApplication_HiltComponents.ActivityRetainedC → com.kmdhtsh.myapplication.MainApplication_HiltComponents.ActivityC]

エラーを直訳すると、「Context は@Providesアノテーションメソッドがないと提供できません」と出ています。
解決策として、ActivityProvidesModuleクラスのprovideSharedPreferencesWrapperメソッドにも、同様のアノテーションを付与します。

ActivityProvidesModule.kt
/**
 * SharedPreferencesWrapperの提供
 */
@Provides
fun provideSharedPreferencesWrapper(@ActivityContext context: Context): SharedPreferencesWrapper {
    return SharedPreferencesWrapper(context)
}

これでビルドエラーが解消されるはずです。