fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

WorkManagerのWorkerにDagger2でDIする

今回の前提:

implementation "androidx.work:work-runtime-ktx:2.3.1"
api "com.google.dagger:dagger:2.26"
kapt "com.google.dagger:dagger-compiler:2.26"

WorkerWorkManagerによって生成される。デフォルトではContextWorkerParametersが渡されて初期化される。初期化の際、それ以外に必要なオブジェクトを受け取りたいときにはWorkerFactoryを実装してWorkManagerに渡してやる必要がある。独自のWorkerFactoryを実装して使えるようにするには、次のことが必要。

  • AndroidManifest.xmlWorkManagerInitializerを止める。例えば: implemented scheduled worker for record schedule checker · akihito104/nasnen@c3b1d22 · GitHub
  • ApplicationクラスにConfiguration.Providerを実装する。Configuration.BuilderWorkerFactoryをセットしてbuild()し、Configuration.Provider.getWorkManagerConfiguration()から返せるようにする。
  • WorkManager.getInstance(Context)を使ってWorkManagerを取得する。Contextを受け取らない方はDeprecatedになっている。

戦略はViewModelの時と大体同じで、Daggerが作ってくれるProviderのMapをWorkerFactoryに注入して、WorkerFactory.createWorker()の中でProviderを取り出して生成したクラスを返してやればよい。ただし、ViewModelの場合はDIが知っているオブジェクトしか取り扱わないのでDIが初期化までやってくれたのだが、Workerの場合はWorkerFactory.createWorker()に渡されるContextWorkerParametersを渡して初期化する必要があるので勝手が違う。

Providerから出てきたものにContextWorkerParametersを渡して初期化をしたいので、Providerから出てくるものは具体的なWorkerを作るFactoryである必要がある。例えば:

class HogeWorker(
    private val repository: HogeRepository,
    context: Context,
    param: WorkerParameters
) : CoroutineWorker(context, param) { // 継承するListenableWorkerは何でもいいです
// ...
  class Factory @Inject constructor(
      private val repository: HogeRepository
  ) : HogeWorkerFactory.Factory {
      override fun create(
          appContext: Context,
          workerParameters: WorkerParameters
      ): ListenableWorker = HogeWorker(repository, appContext, workerParameters)
  }
}

よってDaggerからWorkerFactoryに注入するMapは、具体的なWorkerのClassとそのFactoryとを紐づけたもの(例えば、Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<Factory>>)になる。このMapを作るためのBinderは次のような感じ。

@Binds
@IntoMap
@WorkerKey(HogeWorker::class)
fun bindHogeWorker(worker: HogeWorker.Factory): HogeWorkerFactory.Factory