Search code examples
javaandroiddependency-injectiondagger-2dagger

How to get Component already initialized with Application context


I just started using dagger2 and in spite of having read and documented myself to get the main ideas, I don't understand which would be the proper way of injecting the application's context.

I know that there are similar questions like this or this but this uses AndroidInjector and I'm getting even more lost. What I would like to understand is once the ContextComponent has been initialised and is a Singleton, how can I retrieve the instance of ContextComponent (that contains the Application's context) in order to call ContextComponent.getSharedPreferenceSpecific() and get an instance of SharedPreferenceManagerSpecific initialised with the Application's context?

The are the classes I have created, SharedPreferenceManagerSpecific it's just to understand how to inject a context to a class.

The error I get when the code of activity is executed i:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.amgdeveloper.articleReader/com.amgdeveloper.articleReader.ui.MainActivity}: java.lang.IllegalStateException: com.amgdeveloper.articleReader.dagger.ContextModule must be set at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)

ContextModule.kt

@Module
class ContextModule(private val app: Application) {

    @Provides
    @Singleton
    fun provideContext(): Context = app

}

ContextComponent.tk

@Singleton
@Component(modules = [ContextModule::class])
interface ContextComponent {
    fun getSharedPreferenceSpecific ():SharedPreferenceManagerSpecific

    fun inject (application: MyApplication)

    fun inject (application: MainActivity)

MyApplication.kt

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        DaggerContextComponent.builder().contextModule(ContextModule(this)).build()
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

 val component = DaggerContextComponent.builder().build()
    val sharedPreference = component.getSharedPreferenceSpecific()
    }

}

SharedPreferenceManagerSpecific.kt

public class SharedPreferenceManagerSpecific () {
    lateinit var myContext : Context

    @Inject constructor( context: Context) : this() {
        myContext=context
    }

    fun saveIntoSharedPreferences(){
    ...
    }

}

Solution

  • Dagger doesn't store component instances anywhere. Not even @Singleton components: as far as Dagger is concerned, @Singleton is just another scope. In order to create a component in your Application class and use it in your Activity class, you will need to store it somewhere yourself.

    In most Android apps using Dagger, the component is stored in the Application:

    class MyApplication : Application() {
        
        // You could set a lateinit var in onCreate instead
        // if you don't use any ContentProviders.
        val component by lazy {
            DaggerContextComponent.builder().contextModule(ContextModule(this)).build()
        }
        
    }
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val component = (application as MyApplication).component
            val sharedPreference = component.getSharedPreferenceSpecific()
        }
    
    }