Dagger Androidの導入方法
AndroidのDIコンテナとしてメジャーなDagger
そのAndroid用の拡張版であるDagger-Androidの導入方法を今更ながら、内部の実装に踏み込まず、純粋にプロジェクトに導入するにはどうするべきかというスコープでまとめてみます。
Daggerライブラリのインポート
Daggerを使うために必要なライブラリをプロジェクトに読み込みます。
build.gradle(module)
plugins { id("kotlin-kapt") } ...... dependencies { ...... implementation("com.google.dagger:dagger:2.25.2") implementation("com.google.dagger:dagger-android:2.25.2") implementation("com.google.dagger:dagger-android-support:2.25.2") kapt("com.google.dagger:dagger-compiler:2.25.2") kapt("com.google.dagger:dagger-android-processor:2.25.2") ...... }
Daggerを使ってInjectしたいクラスを作成
今回はHelloというクラスを対象のActivityやFragmentなどにInjectしたいとします。
Helloクラスは次のようになっています。
class Hello(private val name: String = "Dagger") { fun echo() = println("Hello $name") }
このHelloクラスを普通にインスタンスを作成して呼び出す場合、次のようになります。
val hello = Hello() hello.echo() // (Hello Dagger) と出力される。
このHelloクラスをDaggerを使って、次のように呼び出せるようにしていきます。
@Inject lateinit var hello: Hello hello.echo() // (Hello Dagger) と出力される。
Helloクラスを提供するModuleを作成
まず、Helloクラスを任意の場所にInjectできるようにするため、HelloModule(名前は任意)を作成します。
@Module object HelloModule { @Singleton @Provides fun provideHello() = Hello() }
ActivityModuleの作成
HelloクラスをInjectできるようにActivityについて宣言しておきます。
@Module abstract class ActivityModule { @ContributesAndroidInjector internal abstract fun contributeMainActivity(): MainActivity() }
AppComponentの作成
上記のHelloModuleとActivityModuleを参照するAppComponentを作成します。
@Singleton @Component( module = [ AndroidInjectionModule::class, // Dagger-Androidを使用する場合は必須 ActivityModule::class, HelloModule::class ] ) interface AppComponent : AndroidInjector<ApplicationWrapper>
Applicationクラスの作成
ここまでで作成したComponentとModuleをアプリから参照できるようにしていきます。
次のようなApplicationクラスを継承したクラスを作成し、Manifestに登録します。
class ApplicationWrapper : Application(), HasAndroidInjector { @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> { DaggerAppComponent .create() .inject(this) return androidInjector } }
ここまでの作業で、Dagger-Androidを使用する下準備は完了です。
ActivityへInjectする。
それでは、MainActivityのメンバ変数にHelloクラスをInjectしていきます。
class MainActivity : AppCompatActivity(), HasAndroidInjector // 必須 { @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> // 必須 @Inject lateinit var hello: Hello // ここへHelloクラスをInjectする override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) // Inject super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } // ↓追記 override fun androidInjector(): AndroidInjector<Any> { return androidInjector } }
これで、helloにHelloクラスがinjectされます。
FragmentへInjectする
Fragmentに対してInjectする場合ですが、こちらについてもまずはFragmentのModuleの作成を行います。
@Module abstract class FragmentModule { @ContributesAndroidInjector internal abstract fun contributeMainFragment(): MainFragment }
これでMainFragmentの定義ができました。
これをAppComponentに追加します。
@Singleton @Component( module = [ AndroidInjectionModule::class, ActivityModule::class, FragmentModule::class, // 追加 HelloModule::class ] ) interface AppComponent : AndroidInjector<ApplicationWrapper>
あとは、Fragmentで呼び出すだけです。
class MainFragment : Fragment() { companion object { @Suppress("unused") private val TAG: String = MainFragment::class.java.simpleName } private lateinit var binding: FragmentMainBinding @Inject lateinit var hello: Hello override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false) return binding.root } override fun onAttach(context: Context) { super.onAttach(context) AndroidSupportInjection.inject(this) // 追記 } }
これだけで使用することができます。 これで、基本的な呼び出し方は完了です。
Contextが欲しい場合
例えば次のようなクラスがあるとします。
class Colors(private val context: Context) { fun getWhite(): Int = ContextCompat.getColor(context, android.R.color.white) fun getBlack(): Int = ContextCompat.getColor(context, android.R.color.black) }
このColorsクラスはコンストラクタの引数で渡されたContextを元にColorの値を返却します。
このクラスをDaggerを使ってInjectする場合、Contextを提供するProvideメソッドを作成する必要があります。
Contextを提供できるようにするため、まずはAppComponentを編集します。
interface AppComponent : AndroidInjector<ApplicationWrapper> { // 追加 @Component.Factory interface Factory : AndroidInjector.Factory<ApplicationWrapper> // ここまで }
この変更を行うとApplicationを継承したクラスがビルドできなくなるため、次のような修正を加えます。
override fun androidInjector(): AndroidInjector<Any> { DaggerAppComponent .factory() // 追記 .create(this) // 引数にthisを設定 .inject(this) return androidInjector }
ここまでできたら、あとはColorsクラスとContextをProvideするメソッドを作成します。
@Module object AppModule { @Singleton @Provides fun provideContext(applicationWrapper: ApplicationWrapper): Context = applicationWrapper.applicationContext @Singleton @Provides fun provideColors(context: Context) = Colors(context) }
これで、準備完了です。 呼び出したいActivityやFragmentでInjectしてあげることでColorsクラスを使用することができます。
以上