Dagger Hilt は Android 用に様々な場面で使いやすいようになっていて、WorkManager を使った場合にも Dagger Hilt で DI が実現できるようになっています。
依存関係
Dagger Hilt と WorkManager を依存関係に追加します。2021/12 時点で Dagger Hilt の KSP 対応は完了していないので、kapt を使います。
buildscript { dependencies { // Hilt Gradle Plugin への依存を追加 classpath("com.google.dagger:hilt-android-gradle-plugin:2.40.3") } }
plugins { // kapt を適用 kotlin("kapt") // Hilt Gradle Plugin を適用 id("dagger.hilt.android.plugin") } dependencies { // Dagger-Hilt の依存 implementation("com.google.dagger:hilt-android:2.40.3") kapt("com.google.dagger:hilt-android-compiler:2.40.3") // AndroidX Hilt の依存 implementation("androidx.hilt:hilt-common:1.0.0") kapt("androidx.hilt:hilt-compiler:1.0.0") implementation("androidx.hilt:hilt-work:1.0.0") // WorkManager の依存 implementation("androidx.work:work-runtime-ktx:2.7.1") } kapt { correctErrorTypes = true }
Worker の定義
Worker の定義は次の通りで、@HiltWorker
, @AssistedInject
, @Assisted
の 3 つのアノテーションを使います。
- @HiltWorker
: クラス定義に使うアノテーション
- @AssistedInject
: Worker のコンストラクタに使うアノテーション
- @Assisted
: WorkManager 内部で Worker に渡される Context と WorkerParameters に対して使うアノテーション
@HiltWorker class SampleWorker @AssistedInject constructor( @Assisted applicationContext: Context, @Assisted params: WorkerParameters, ) : Worker(applicationContext, params) { override fun doWork(): Result { // ... } }
この Worker に対し、さらに別のオブジェクトを差し込みたい場合は単純にコンストラクタに差し込みたいオブジェクトを追加していくだけです。
@HiltWorker class SampleWorker @AssistedInject constructor( @Assisted applicationContext: Context, @Assisted params: WorkerParameters, // なんらかの data source レイヤのクラスを注入する(別途この data source の実体を提供するための module の宣言などは必要) private val sampleDataSource: SampleDataSource, ) : Worker(applicationContext, params) { override fun doWork(): Result { // ... } }
WorkerFactory の追加
WorkManager では Work クラスの生成はほぼ自分で記述することがありません。Worker の定義がシンプルでコンストラクタの引数に Context と WorkerParameters しかない場合は、WorkRequest を経由して自動で Worker がインスタンス化されます。一方で引数に Context と WorkerParameters 以外のものがある場合は WorkManager が自動で Worker を生成できないため、Worker を生成するための手順を実装し WorkManager に教えてあげる必要があります。
Dagger Hilt を使う場合、先ほどの SampleWorker
を生成するには Dagger が知っている object graph から SampleDataSource
の実体を取り出して Worker にわたす手順が必要ですが、その手順は hilt-work が用意してくれていて、アプリの実装としては Application
クラスを拡張し必要なインタフェースの実装を追加するだけになります。
@HiltAndroidApp // androidx.work の Configuration.Provider を実装する class ExampleApplication : Application(), Configuration.Provider { // Dagger Hilt 用の WorkerFactory @Inject lateinit var workerFactory: HiltWorkerFactory // Dagger Hilt 用の WorkerFactory を設定して Configuration を返す override fun getWorkManagerConfiguration() = Configuration.Builder() .setWorkerFactory(workerFactory) .build() }
この設定のため AndroidManifest.xml では WorkManager の default initializer を無効化しておきます。default initializer の無効化手順は、アプリで App Startup を使っているかどうかによって変わります。WorkManager は内部で App Startup の仕組みを使っているため、アプリが WorkManager 以外の用途で App Startup を使っている場合は、その App Startup から WorkManager のみを無効化する必要があります。
<!-- アプリで他に AppStartup を使わない場合 --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" tools:node="remove"> </provider>
<!-- アプリで他に AppStartup を使っている場合 --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <meta-data android:name="androidx.work.WorkManagerInitializer" android:value="androidx.startup" tools:node="remove" /> </provider>
WorkRequest を使って動かす
あとは Worker を動かす手順を実装するだけです。注意点として、WorkManager
のインスタンスは必ず WorkManager.getInstance(Context)
を使って取得します(引数のない getInstance メソッドでは WorkerFactory の設定などが無視されてしまう)。
val delay = 1000L val params = Data.Builder() .putInt("number", 1) .build() val workManager = WorkManager.getInstance(Context) val work: OneTimeWorkRequest = OneTimeWorkRequest.Builder(SampleWorker::class.java) .setInitialDelay(delay, TimeUnit.MILLISECONDS) .setInputData(params) .build() workManager.beginUniqueWork("sample_work", REPLACE, work).enqueue()