Search

Dagger: Missing application component

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.

Join the discussion