I'm using Dagger Hilt for DI in my application. Whenever I try to inject my own dependencies in the constructor, WorkManager fails to initialize. But it works when I delete my own dependencies.
I set up my worker class for DI as follows:
UploadTripsWorker class:
import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.myapp.app.di.ChildWorkerFactory
import com.myapp.app.repo.UserRepositoryInterface
import com.myapp.app.repo.local.LocalDriverRepositoryInterface
import com.google.firebase.auth.FirebaseAuth
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@HiltWorker
class UploadTripsWorker @AssistedInject constructor(
@Assisted appContext: Context,
@Assisted workerParams: WorkerParameters,
private val userRepository: UserRepositoryInterface,
private val localUserRepository: LocalDriverRepositoryInterface,
private val auth: FirebaseAuth
) : CoroutineWorker(appContext, workerParams) {
@AssistedFactory
interface Factory : ChildWorkerFactory {
override fun create(appContext: Context, params: WorkerParameters): UploadTripsWorker
}
//doWork logic here
}
MyWorkerFactory class:
import android.content.Context
import androidx.work.ListenableWorker
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
import javax.inject.Inject
import javax.inject.Singleton
import javax.inject.Provider
@Singleton
class MyWorkerFactory @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 interface:
import android.content.Context
import androidx.work.WorkerParameters
import com.myapp.app.workers.UploadTripsWorker
interface ChildWorkerFactory {
fun create(appContext: Context, params: WorkerParameters): UploadTripsWorker
}
WorkerBindingModule Interface:
import com.myapp.app.workers.UploadTripsWorker
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoMap
@Module
@InstallIn(SingletonComponent::class)
interface WorkerBindingModule {
@Binds
@IntoMap
@WorkerKey(UploadTripsWorker::class)
fun bindUploadTripsWorker(factory: UploadTripsWorker.Factory): ChildWorkerFactory
}
annotation class WorkerKey:
import androidx.work.ListenableWorker
import dagger.MapKey
import kotlin.reflect.KClass
@MapKey
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
Inside AppModule, I provide the necessary dependencies:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton
@Provides
fun provideFirebaseAuth(): FirebaseAuth = FirebaseAuth.getInstance()
@Singleton
@Provides
fun provideNormalUserRepository(db: FirebaseFirestore, storage: FirebaseStorage, auth: FirebaseAuth) = UserRepository(db,storage, auth) as UserRepositoryInterface
@Singleton
@Provides
fun provideLocalDriverRepository(driverDao: DriverDao): LocalDriverRepositoryInterface {
return LocalDriverRepository(driverDao)
}
}
In manifest I used:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
And this is my Application class:
@HiltAndroidApp
class MyAppApplication : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
"location",
"Location Service",
NotificationManager.IMPORTANCE_LOW
)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
WorkManager.initialize(this, workManagerConfiguration)
}
}
Whenever I delete the userRepository
, localUserRepository
and auth
from the UploadTripsWorker
's constructor, worker starts with no errors.
So this works with no issiues:
@HiltWorker
class UploadTripsWorker @AssistedInject constructor(
@Assisted appContext: Context,
@Assisted workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {
But when I add:
private val userRepository: UserRepositoryInterface,
private val localUserRepository: LocalDriverRepositoryInterface,
private val auth: FirebaseAuth
to constructor of UploadTripsWorker
, it doesn't work. Throws an error:
Could not instantiate com.myapp.app.workers.UploadTripsWorker (Ask Gemini)
java.lang.NoSuchMethodException: com.myapp.app.workers.UploadTripsWorker.<init> [class android.content.Context, class androidx.work.WorkerParameters]
at java.lang.Class.getConstructor0(Class.java:3325)
at java.lang.Class.getDeclaredConstructor(Class.java:3063)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:94)
at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:243)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:144)
at androidx.work.impl.utils.SerialExecutorImpl$Task.run(SerialExecutorImpl.java:96)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Could not create Worker com.myapp.app.workers.UploadTripsWorker
And in my gradle:
//Dagger-Hilt
implementation "com.google.dagger:hilt-android:2.49"
kapt "com.google.dagger:hilt-compiler:2.44"
implementation 'androidx.hilt:hilt-common:1.2.0'
implementation 'androidx.hilt:hilt-work:1.2.0'
//Work
implementation "androidx.work:work-runtime-ktx:2.9.0"
plugins {
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
}
I solved this issue by adding kapt("androidx.hilt:hilt-compiler:1.2.0")
into my build.gradle(Module) file. So just kapt "com.google.dagger:hilt-compiler:2.44"
is not enough, we need both.