androidkotlindagger-2dagger-hiltandroid-workmanager

Could not instantiate worker error when using WorkManager and Dagger-Hilt


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'
}

Solution

  • 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.