Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enable to setup android workmanager with dagger to inject repository in android worker

Please find, What is going wrong with below setup for dagger 2 and android workmanager.

WorkerKey.kt

import androidx.work.ListenableWorker
import dagger.MapKey
import kotlin.reflect.KClass

@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)

DaggerWorkerFactory.kt

class DaggerWorkerFactory @Inject constructor(
    private val workerFactories: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<ChildWorkerFactory>>
) : WorkerFactory() {

    override fun createWorker(
        appContext: Context,
        workerClassName: String,
        workerParameters: WorkerParameters
    ): ListenableWorker? {
        val foundEntry =
            workerFactories.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) }
        val factoryProvider = foundEntry?.value
            ?: throw IllegalArgumentException("Unknown worker class name : $workerClassName")
        return factoryProvider.get().create(appContext, workerParameters)
    }
}

ChildWorkerFactory.kt

import android.content.Context
import androidx.work.ListenableWorker
import androidx.work.WorkerParameters

interface ChildWorkerFactory {
    fun create(appContext: Context, params: WorkerParameters): ListenableWorker
}

WorkerModule.kt

import com.hardik.core.di.ChildWorkerFactory
import com.hardik.core.di.WorkerKey
import com.hardik.spendy.PrepopulateCategoryWorker
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap

@Module
interface WorkerModule {

    @Binds
    @IntoMap
    @WorkerKey(PrepopulateCategoryWorker::class)
    fun bindPrepopulateCategoryWork(factory: PrepopulateCategoryWorker.Factory): ChildWorkerFactory
}

AppComponent.kt

@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        DatabaseModule::class,
        UseCaseModule::class,
        ViewModelModule::class,
        WorkerModule::class,
        ActivityModule::class
    ]
)
interface AppComponent : AndroidInjector<SpendyApplication> {

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: SpendyApplication): Builder

        fun build(): AppComponent
    }

    override fun inject(app: SpendyApplication)
}

PrepopulateCategoryWorker.kt

import android.content.Context
import android.util.Log
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.hardik.core.di.ChildWorkerFactory
import com.hardik.repository.Repository
import javax.inject.Inject
import javax.inject.Provider

class PrepopulateCategoryWorker(
    private val appContext: Context,
    private val workerParams: WorkerParameters,
    private val repository: Repository
) : Worker(appContext, workerParams) {

    override fun doWork(): Result {

        Log.i("Hardik", "Repository injected : $repository")
        return Result.success()
    }

    class Factory @Inject constructor(
        private val repository: Provider<Repository>
    ) : ChildWorkerFactory {

        override fun create(appContext: Context, params: WorkerParameters): ListenableWorker {
            return PrepopulateCategoryWorker(
                appContext,
                params,
                repository.get()
            )
        }
    }
}

MainActivity.kt

class MainActivity : DaggerAppCompatActivity() {

     @Inject
    lateinit var workerFactory: WorkerFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
         WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
    }
}

HomeFragment.kt

private fun startPrepopulate() {
        val request = OneTimeWorkRequestBuilder<PrepopulateCategoryWorker>().build()
        val workManager = WorkManager.getInstance(requireContext())
        workManager.enqueue(request)
    }

Error Logs

e: C:\git\Spendy\app\build\tmp\kapt3\stubs\debug\com\hardik\spendy\di\AppComponent.java:8: error: [Dagger/MissingBinding] androidx.work.WorkerFactory cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.hardik.spendy.SpendyApplication> {
                ^
      androidx.work.WorkerFactory is injected at
          com.hardik.spendy.ui.activity.MainActivity.workerFactory
      com.hardik.spendy.ui.activity.MainActivity is injected at
          dagger.android.AndroidInjector.inject(T) [com.hardik.spendy.di.AppComponent ? com.hardik.spendy.di.ActivityModule_ContributeMainActivity.MainActivitySubcomponent]
like image 759
Hardik Bambhania Avatar asked Oct 17 '25 19:10

Hardik Bambhania


1 Answers

you have to tell Dagger how to inject the worker factory as well. Add

 @Binds
 fun bindWorkManagerFactory(factory: DaggerWorkerFactory): WorkerFactory

to your WorkerModule

like image 127
Blackbelt Avatar answered Oct 20 '25 09:10

Blackbelt