google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.46k stars 2.02k forks source link

java.lang.NoSuchMethodException when trying to schedule WorkManager #3586

Closed zskamljic closed 2 years ago

zskamljic commented 2 years ago

I'm trying to schedule a job using the work manager, that's injected by Hilt, but I'm getting the exception mentioned in the title.

I'm trying to schedule work in my ViewModel like so:

@HiltViewModel
class WorkViewModel @Inject constructor(
    private val workManager: WorkManager,
) : ViewModel() {
    private fun scheduleWork() {
        val enrolWorkRequest = OneTimeWorkRequestBuilder<EnrolWorker>()
            .build()

        workManager.enqueue(enrolWorkRequest)
    }
}

My Application class is declared as following:

@HiltAndroidApp
class App : Application(), Configuration.Provider {
    @Inject
    lateinit var workerFactory: HiltWorkerFactory

    override fun getWorkManagerConfiguration(): Configuration {
        return Configuration.Builder()
            .setMinimumLoggingLevel(Log.DEBUG)
            .setWorkerFactory(workerFactory)
            .build()
    }
}

My AndroidManifest.xml:

<?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
        android:name=".App">
        <!-- other properties -->
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data
                android:name="androidx.work.WorkManagerInitializer"
                android:value="androidx.startup"
                tools:node="remove" />
        </provider>
    </application>
</manifest>

The worker itself:

@HiltWorker
class EnrolWorker @AssistedInject constructor(
    @Assisted appContext: Context,
    @Assisted workerParams: WorkerParameters,
    private val call: SomeUseCase,
) : CoroutineWorker(appContext, workerParams) {
    override suspend fun doWork(): Result {
        call()

        return Result.success()
    }
}

The dependencies:

implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
kapt 'androidx.hilt:hilt-compiler:1.0.0'
implementation 'androidx.hilt:hilt-work:1.0.0'
implementation "androidx.work:work-runtime-ktx:$work_version"
implementation 'androidx.hilt:hilt-work:1.0.0'

Trying to debug, it appears that getWorkManagerConfiguration is never called, I would assume this would be the reason for missing injections.

bcorso commented 2 years ago

I'm trying to schedule a job using the work manager, that's injected by Hilt, but I'm getting the exception mentioned in the title.

Can you post the entire error message? Typically, a NoSuchMethodException is accompanied by the name of the method that it was trying to call.

Trying to debug, it appears that getWorkManagerConfiguration is never called, I would assume this would be the reason for missing injections.

I don't think this is related to getWorkManagerConfiguration not being called or missing injections. NoSuchMethodException occurs when something tries to access a method at runtime via reflection but the method doesn't actually exist. It's likely an issue with your dependency versions, where the library making the reflection call is expecting a method that doesn't exist in the version of the dependency you're using.

I'm going to close this though, since don't think this is a Hilt issue. However, feel free to post back if you think there's something actionable for us to do in Hilt.

zskamljic commented 2 years ago

@bcorso issue has been reported before by other people, and was closed like this one was. The stack trace is as follows:

Could not instantiate com.lablizards.lwm.background.EnrolWorker
java.lang.NoSuchMethodException: com.lablizards.lwm.background.EnrolWorker.<init> [class android.content.Context, class androidx.work.WorkerParameters]
    at java.lang.Class.getConstructor0(Class.java:2363)
    at java.lang.Class.getDeclaredConstructor(Class.java:2201)
    at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:95)
    at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:245)
    at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:137)
    at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)

Using this code works, it also calls Configuration.Provider:

WorkManager.getInstance(getApplication()).enqueueUniqueWork(
    Constants.Work.ENROL_NAME, ExistingWorkPolicy.KEEP, enrolWorkRequest
)

But using the injected WorkManager in view model does not seem to invoke the Configuration.Provider in the application class, resulting in what I assume is using the default WorkManager factory.

danysantiago commented 2 years ago

Can you share your @Provides of WorkManager does it also go through WorkManager.getInstance ?

As you have mentioned the error NoSuchMethodException is indeed because WorkManager can't construct the Worker, likely because HiltWorkerFactory is not being set in the configuration.

zskamljic commented 2 years ago

I did not create a @Provides method for WorkManager, as it was not mentioned anywhere in the docs, maybe this is the underlying issue?