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