Modularization is a great tool for building an app at scale, however care must be taken to ensure that resources shared between modules are properly instantiated.
WorkManager provides a default configuration that is applied on app start up. This is often convenient, but in a modularized app where different modules often have different dependencies,
WorkManager’s default configuration may not be enough. This blog post details how we set up
WorkManager for use in the multi module Now in Android app.
WorkManager exists as a singleton instance. To provide custom configuration for this instance in a single module app, you can implement a
Configuration.Provider that provides parameters for the
Executor used to perform work. You can also implement a
WorkerFactory to aid in dependency injection when using
WorkManager with Hilt.
Things get more nuanced in a multi-module app. Which module should be responsible for configuring the
WorkManager instance? If such a module exists, wouldn’t it need to depend on all modules that need workers? Wouldn’t that break the “low cohesion” principle when modularizing apps? It would, so instead, we lean on another software engineering principle: delegation.
Creating a DelegatingWorker
WorkerFactory instances can create
Worker instances at runtime given:
When you couple this with Hilt entry points, it becomes possible to dynamically delegate to
Worker instances lazily without having to implement the
Configuration.Provider in a central location. The skeleton for such an implementation follows:
The above code determines what
Worker the app should delegate to. To do so, it reads the fully qualified name of the
Worker class from the
WorkerParameters. Once it has that, it takes the
HiltWorkerFactoryEntryPoint from the application
Context and uses it to instantiate the given
See the following snippet for an example of a utility method you could use to pass the fully qualified name of the worker to the
DelegatingWorker via the
To use the above function, create a
WorkRequest targeting the
DelegatingWorker, and then add any other metadata needed for the work in the delegated
Worker. In the snippet to follow, the delegated
Worker is the
Finally, enqueue the work as usual:
Use of a
DelegatingWorker instance is useful in more than just multi-module apps. It is also useful for libraries who need to use
WorkManager but cannot pass their dependencies easily to the apps that depend on them.
A useful rule of thumb is, if you do not have convenient access to the
Application instance in app and you need to call on
WorkManager, use a delegating worker to lazily create your actual
Worker with the necessary
Leave a Reply