Skip to content

Instantly share code, notes, and snippets.

@luanmm
Last active December 1, 2023 20:01
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luanmm/85dd8217ed3f7384e6bab075a8ab7a61 to your computer and use it in GitHub Desktop.
Save luanmm/85dd8217ed3f7384e6bab075a8ab7a61 to your computer and use it in GitHub Desktop.
Android WorkManager dependency injection (with Dagger)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
...>
<application
...>
...
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="<your-app-package>.workmanager-init"
android:enabled="false"
android:exported="false"
tools:replace="android:authorities" />
</application>
</manifest>
import androidx.work.Worker
object AndroidWorkerInjection {
fun inject(worker: Worker) {
val application = worker.applicationContext
if (application !is HasWorkerInjector) {
throw RuntimeException(
"${application.javaClass.canonicalName} does not implement ${HasWorkerInjector::class.java.canonicalName}")
}
val workerInjector = (application as HasWorkerInjector).workerInjector()
checkNotNull(workerInjector) { "${application.javaClass}.workerInjector() return null" }
workerInjector.inject(worker)
}
}
import androidx.work.Worker
import dagger.Module
import dagger.android.AndroidInjector
import dagger.multibindings.Multibinds
@Module
abstract class AndroidWorkerInjectionModule {
@Multibinds
abstract fun workerInjectorFactories(): Map<Class<out Worker>, AndroidInjector.Factory<out Worker>>
}
import androidx.work.Configuration
import androidx.work.WorkManager
import androidx.work.Worker
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.DaggerApplication
import javax.inject.Inject
class App : DaggerApplication(), HasWorkerInjector {
@Inject
lateinit var workerInjector: DispatchingAndroidInjector<Worker>
override fun workerInjector(): AndroidInjector<Worker> = workerInjector
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
DaggerAppComponent.builder().create(this)
override fun onCreate() {
super.onCreate()
initWorkManager()
}
private fun initWorkManager() {
val config = Configuration.Builder()
.setWorkerFactory(DaggerWorkerFactory()) // Overrides default WorkerFactory
.build()
WorkManager.initialize(this, config)
}
}
import dagger.Component
import dagger.android.AndroidInjector
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
AndroidWorkerInjectionModule::class, // Worker injection module referenced in the AppComponent
AppModule::class
])
interface AppComponent : AndroidInjector<App> {
@Component.Builder
abstract class Builder : AndroidInjector.Builder<App>()
}
import android.content.Context
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
class DaggerWorkerFactory : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
val constructor = Class.forName(workerClassName)
.asSubclass(Worker::class.java)
.getDeclaredConstructor(Context::class.java, WorkerParameters::class.java)
return constructor.newInstance(appContext, workerParameters)
.apply { AndroidWorkerInjection.inject(this) }
}
}
import androidx.work.Worker
import dagger.android.AndroidInjector
interface HasWorkerInjector {
fun workerInjector(): AndroidInjector<Worker>
}
import android.content.Context
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters
import javax.inject.Inject
class SampleWorker(context: Context, params: WorkerParameters)
: Worker(context, params) {
@Inject
lateinit var sampleRepository: SampleRepository
override fun doWork(): Result {
val myData = sampleRepository.getMyData()
// ...
}
companion object {
const val TAG = "SampleWorker"
}
}
@luanmm
Copy link
Author

luanmm commented Dec 19, 2018

Code inspired in ideas from:

Discussion: google/dagger#1183.

@michzio
Copy link

michzio commented Dec 21, 2018

I don' t know way but this solution doesn't work for me
My app crash on on this line of code workerInjector.inject(worker)

java.lang.IllegalArgumentException: No injector factory bound for Class<com.myapp.workers.ProposedDatesEmailWorker>
at dagger.android.DispatchingAndroidInjector.inject(DispatchingAndroidInjector.java:163)
at com.myapp.base.di.injectable_worker_v2.AndroidWorkerInjection.inject(AndroidWorkerInjection.kt:16)
at com.myapp.base.di.injectable_worker_v2.DaggerWorkerFactory.createWorker(DaggerWorkerFactory.kt:22)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:76)
at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:190)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:124)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)

@gbzarelli
Copy link

gbzarelli commented Feb 8, 2019

I don' t know way but this solution doesn't work for me
My app crash on on this line of code workerInjector.inject(worker)

java.lang.IllegalArgumentException: No injector factory bound for Class<com.myapp.workers.ProposedDatesEmailWorker>
at dagger.android.DispatchingAndroidInjector.inject(DispatchingAndroidInjector.java:163)
at com.myapp.base.di.injectable_worker_v2.AndroidWorkerInjection.inject(AndroidWorkerInjection.kt:16)
at com.myapp.base.di.injectable_worker_v2.DaggerWorkerFactory.createWorker(DaggerWorkerFactory.kt:22)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:76)
at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:190)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:124)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)

I found the following solution,
It's not like wanting it to be, but it's working:

Tanks @luanmm - this article helped me so much

@vudunguit
Copy link

vudunguit commented Mar 20, 2019

it doesn't work, app crash with log message No injector factory bound for Class<com.android.vntalk.data.service.UploadWorker>

@serhii-pokrovskyi
Copy link

Hello, this is very good, but:
What about RxWorker?
This solution does not support it.

@o-vitaliy
Copy link

@ContributesAndroidInjector
abstract UploadWorker contributeUploadWorkerr();

solves No injector factory bound for Class<com.android.vntalk.data.service.UploadWorker>

@gfreivasc
Copy link

@serhii-pokrovskyi This implementation considers Worker as the generic worker class, but it's just the base regular worker. The more generic approach would be to use ListenableWorker. I didn't try but swapping Worker for ListenableWorker in the composition classes should do make it work for RxWorker.

@yudikarma
Copy link

@ContributesAndroidInjector
abstract UploadWorker contributeUploadWorkerr();

where i add this part to solve No injector factory bound for Class<com.android.vntalk.data.service.UploadWorker> ??

@hoangnguyen92dn
Copy link

I got same error No injector factory bound for Class<com.android.vntalk.data.service.UploadWorker>. Please help to update your configuration.

@luanmm
Copy link
Author

luanmm commented Mar 21, 2020

Hello, people!

Sorry about the long delay in my reply for this gist.

It was created as a first try to handle dependency injection in WorkManager. But it was implemented even before the stable release of the WorkManager. So it is not really updated and may not work in the newer library version.

I really didn't liked the way WorkManager was designed without this look for dependency injection. But now the recommended way to do it is something like this tutorial: https://proandroiddev.com/dagger-2-setup-with-workmanager-a-complete-step-by-step-guild-bb9f474bde37. It uses AssistedInject library to make things work more easily.

Personally, I think that may have a better way to instantiate workers without using AssistedInject and doing the things in a simplified way (as I tried at first). But my knowledge about the inner structure of Dagger does not allow me to design a better solution. So, for now, I am using the recommended tutorial in my personal projects aswell.

Sorry for disappointing, if it is the case who looked for someting more simplified, but currently I don't know a better way to do it. If someone knows, feel free to share or post an updated gist.

Thank you all for your considerations and for helping each other!

Best regards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment