Dagger is probably the most used Dependency Injection framework for Android.
It’s even an advocated solution directly from Google. It simplifies building and providing dependencies across the app. It gives the ability to create specific scopes, modules, components, etc.
This article assumes that Dagger is no longer unknown to you. On the contrary, it will focus on a rather atypical problem that you may encounter during the lifetime of your application.
The error
Have you ever encountered a similar problem?
Caused by: java.lang.NullPointerException: at dagger.internal.Preconditions.checkNotNull (Preconditions.java:48) at dagger.internal.DoubleCheck.get (DoubleCheck.java:47) at com.example.dagger.DaggerApplicationComponent.injectActivity
In Android you usually create a component that lives in your application class because you want an instance of it as long as the app is running. In some cases, you might also want to have the application context available.
Most of the time it’s called ApplicationComponent
.
Under normal conditions, a component can be created in a custom application class. And if you fail to access it and receive a similar error, then it is most likely due to the fact that ApplicationComponent
could not be created.
There could be several reasons, most likely it is a direct error in the code or a missing definition of your custom application class in AndroidManifest
.
If you have checked everything and at first glance it is not clear why it’s still not possible to create your component, then another unusual situation can occur. Your custom application class (let’s call it CustomApplication
) was ignored even though you have defined it in AndroidManifest.
<application
android:name="com.example.CustomApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme" />
On most devices the given definition of application class is respected, but unfortunately some devices have such exotic modifications (occasionally occurs also on regular brands) that there is a chance that your application class will not be used and the system instantiates the base Application
class instead.
Beware that the same situation also happens during the auto backup (restore operation) but this behaviour is well documented on the official Android site.
Possible solution
If such a situation occurs and starts to significantly affect your application (the application crashes right at startup), then you need to be prepared to move your logic and initialization away from your custom application class.
All initialization logic can then be moved to a separate class, let’s name it AppLazyInit
.
@Singleton
class AppLazyInit @Inject constructor(
// Include your dependencies originally in application class
){
fun init(){
// All initialization could be done within this function
}
}
Basically this gives you a separate initialization that can be called arbitrarily, even if the application class itself is not used. However this still does not solve the problem of when and how to create an application component.
There are several ways to create and access an application component. In our case, we choose a singleton named DependencyInjector
.
object DependencyInjector { @GuardedBy("this") private var applicationComponent: ApplicationComponent? = null @Inject var appLazyInit: Lazy<AppLazyInit>? = null /** * Initializes app component (could be called from custom application class). * * @param applicationContext context of current application */ @Synchronized fun initApplicationComponent(applicationContext: Context) { val applicationModule = ApplicationModule(applicationContext) applicationComponent = DaggerApplicationComponent.builder().applicationModule(applicationModule).build() } /** * Gets application component. If it's missing then it's recreated from current application context. * * @param applicationContext context of current application * @return application component */ fun getApplicationComponent(applicationContext: Context): ApplicationComponent { if (applicationComponent == null) { synchronized(this) { if (applicationComponent == null) { initApplicationComponent(applicationContext.applicationContext) } /* * This case could occur when [CustomApplication] class it being ignored by system and * general [Application] class is used. */ applicationComponent?.injectDependencyInjector(this) appLazyInit?.get()?.init() } } return applicationComponent } }
DependencyInjector
can be used across the entire application, also the function initApplicationComponent
could be called from your custom application class and the component can be validly created in advance. However, if the custom application class is ignored then the getApplicationComponent
getter receives the application context as an argument, so you can safely access it and if necessary it’ll create the component from anywhere in the application during the first access.
If you want to test whether your application can work without your custom application class, then there is no easier solution than to delete it from the AndroidManifest
. For simplicity, the class itself can only contain things for debugging, and delegated things in AppLazyInit
can only contain production code.
If you have encountered a similarly unusual problem with Dagger, I hope that this article helped you or at least outlined a possible solution.