Perfectus: Hilt in Modular Application
Recently android introduced a new dependency injection library called Hilt, which is basically a wrapper around Dagger 2, which aims to simplify and standardize how we, android developers are using dependency injection in our applications. You can find the official documentation here.
But how Hilt will behave in Multi Modular Application?
Multi Modular Application Architecture
Our multi-modular application has two features: account and settings and two libraries logger and preferences.
In the Android Studio, we have something like this
logger library is used by both account and settings features, and library preferences is used only by settings feature. The logger is providing a simple way to log stuff and preferences to save data.
You can checkout branch
and see the actual project in work :)
Get Started
First, add the dependency to the project root bradle.build file:
Second, apply plugin and add the dependency in the application modules.
When all dependencies are set, it’s time for annotating Application class with @HiltAndroidApp annotation, this annotation will generate a base class for your application that uses Hilt components.
Providing logger and preferences
Now we want to inject logger in the account and settings modules and preferences only in the settings module (as it’s shown on the diagram above), for that we need to create Hilt Module
Hilt modules are standard Dagger modules that have an additional
@InstallIn
annotation that determines which Hilt component(s) to install the module into.
In the app, let’s create a file called PerfectusModule (actually name doesn’t matter) like this
What is very important here is that you create an object and not a class (as in the case of class your code will compile and run without any errors until you will try to call some method on your injected method, in that case, it will crash as the object will be not injected).
In the code above you can see annotation
@InstallIn(ApplicationComponent::class)
this means that the objects provided by the functions in the class will be available in the whole application, they will be created as soon as Application#onCreate() will be called and will be destroyed as soon as Application#onDesytoy() will be called, there are different components provided by Google which makes our life way easier
If you want your objects to be provided in the Activities and to be destroyed as soon as activities are destroyed just use
@InstallIn(ActivityComponnet::class)
As you can also see provides methods are annotated with @Singleton annotations, this is very important if you want your objects to be created only once; otherwise the object will be recreated each time when it is injected. In our case we want logger and preferences to be created one time and used in all modules where they are injected.
Another useful annotation is
@ApplicationContext context: Context
which will tell Hilt “Hey this method needs Context, please provide it.”
Injecting Logger and Preferences
So now we need to inject the objects and try to call methods on them
with Hilt, it’s easy you just need to annotate your Fragment (can also be Service, BroadcasetReceiver, View) with @AndroidEntryPoint annotation and that’s it. Basically @AndroidEntryPoint annotation marks an Android component class to be setup for injection with the standard Hilt Dagger Android components. And that’s it now just call
@Inject
lateinit var logger: ILogger
and use it wherever you want.
Important to know
As you will see on my branch I have put PerfectusModule in the app module and my app model has dependencies on logger and preferences
implementation project(path: ':libraries:logger')
implementation project(path: ':libraries:preferences')
but even if you move this out, your app module should always know about that dependencies as otherwise, you will get compile-time exceptions, something like
Unable to access private ILogger
Also for now this approach is not working with dynamic features, but it’s still possible to use Hilt with dynamic features, just approach is a bit different you can check this repo for more details.