4

I'm working on a multi module project (Gradle module). I'm using WorkManager in my module. I'm also making use of Dagger for dependency injection. Now I have to use dagger to inject dependencies to my WorkManager. I'm quite familiar with Dagger 2 setup with WorkManager. But the problem I'm facing is, I have to use worker factory to make it compatible with dagger. So that I can inject dependencies with the help of Dagger Multi bindings. But currently the WorkManager configuration in the main module (Main app gradle module) is

      public Configuration getWorkManagerConfiguration() {
        return new Configuration.Builder()
            .setMinimumLoggingLevel(android.util.Log.INFO)
            .build();
      }

Which doesn't use a custom factory. And already several other modules (gradle modules for other features) are using WorkManger without factory. Now If I change this configuration and add a factory, it might break the work manager setup in several other place. Can I make use of a factory only for the WorkManager classes in my module (or only some work manager classes should be initialized via factory, others use default configuration). Is it possible to do? Hope my problem is clear.

doe
  • 971
  • 2
  • 11
  • 27

1 Answers1

3

You can use a DelegatingWorkerFactory and add you're custom WorkerFactory to it.

Your custom WorkerFactory will need to check if the classname passed to the factory is the one it want to handle, if not, just return null and the DelegatingWorkerFactory will revert to the default worker factory using reflection.

Keep in mind that you need to add your custom WorkerFactory each time you initialize WorkManager. If you don't do that and WorkManager tries to fullfill a WorkRequest for your Worker (that is normally handled by the custom WorkerFactory) it will fallback to the default WorkerFactory and fail (probably with a class not found exception).

We are using the DelegatingWorkerFactory in IOsched, the scheduling app used for I/O and the Android Developer Summit. The code of your custom WorkerFactory is going to be something like:

class ConferenceDataWorkerFactory(
    private val refreshEventDataUseCase: RefreshConferenceDataUseCase
) : WorkerFactory() {

    override fun createWorker(
        appContext: Context,
        workerClassName: String,
        workerParameters: WorkerParameters
    ): ListenableWorker? {

        return when (workerClassName) {
            ConferenceDataWorker::class.java.name ->
                ConferenceDataWorker(appContext, workerParameters, refreshEventDataUseCase)
            else ->
                // Return null, so that the base class can delegate to the default WorkerFactory.
                null
        }
    }
}
pfmaggi
  • 6,116
  • 22
  • 43
  • What can we do for dynamic feature modules, where we might want to register a worker factory from a feature module? Your answer is probably useful for people, but doesn't answer the title: we still just have *one* configuration – Ben Butterworth Dec 15 '20 at 18:23
  • I've just tried to use reflection to get the class of the worker factory, but it looks like the classes of dynamic feature modules are not accessible at start up time in the main module: when `getWorkManagerConfiguration` is first called in the app. – Ben Butterworth Dec 15 '20 at 21:14
  • WorkManager is a singleton, you can only have a single configuration. What you can do is to dynamically register you WorkerFactory to a "mainModuleWorkerFactory" (we looked into doing something similar in Plaid in the past https://github.com/android/plaid/pull/682 never merged simply because the backend we used for that functionality was no more available). You also need to remember to delete all the scheduled WorkRequest if your DFM is uninstalled. – pfmaggi Dec 16 '20 at 07:28
  • Thanks for your reply. However, I'm not sure WorkManager be able to find the worker factory if its not registered inside onCreate (as documented) though? WorkManager would still try to use a limited set of factories? I see what you mean though: get the MainWorkManagerFactory inside DataModule, and add a new factory from dynamic feature module, and finally `factory.addFactory(UpvoteStoryWorkerFactory(service))` Not sure if it will work for dynamic feature modules (different file structure in apk?) – Ben Butterworth Dec 16 '20 at 11:00
  • specifically, when WorkManager launches the app to do some work – Ben Butterworth Dec 16 '20 at 11:16
  • I had a look at the PR @pfmaggi, it looks like your `fun provideWorkManager` is scoped to `@FeatureScope`, how do you have this scope started when the app starts? Without this, I think workManager can't start your work. Im not able to see the entire repo because the branch is from "unknown repository". – Ben Butterworth Dec 16 '20 at 18:25
  • The idea is to register in the Application#onCreate a DelegatingWorkerFactory and then add the WorkerFactories for the DFMs to the DelegatingWorkerFactory: https://developer.android.com/reference/kotlin/androidx/work/DelegatingWorkerFactory The problem with the standard DelegatingWF is that it doesn't handle the removal of work (that you take into consideration if a user can remove a DFM). Sorry but it is difficult to be more specific without some code. – pfmaggi Dec 17 '20 at 08:11