Search code examples
androidkotlindagger-2leakcanarydagger

How does Dagger 2 dispose of injected fields when they are no longer needed?


I have a question about injecting multiple fragments into the activity's fields. Currently I have such a set up (all fragments extend DaggerFragment and the activity is DaggerAppCompatActivity):

@Inject
lateinit var fragmentOne: FragmentOne
@Inject
lateinit var fragmentTwo: FragmentTwo
@Inject
lateinit var fragmentThree: FragmentThree

override fun onCreate(...) {
    ...
    startFirstFragment()
}

fun startFirstFragment() {
    supportFragmentManager.beginTransaction()
            .replace(containerId, fragmentOne).commit()
}

fun startSecondFragment() {
    supportFragmentManager.beginTransaction()
            .replace(containerId, fragmentTwo).commit()
}

And everything works fine, until I add LeakCanary, which says that, when I replace first fragment with the second, the instance being replaced leaks through lateinit var fragmentOne as it retains the reference to the first fragment. My question is: when does dagger empty the fields, does it do it correctly and who is to blame: dagger for causing leaks, LeakCanary for false positive leak detection or something else?

ApplicationComponent:

@ApplicationScoped
@Component(
        modules = [
            AndroidSupportInjectionModule::class,
            ActivityBindingModule::class,
            ApplicationModule::class,
            RepositoriesModule::class,
            NetworkModule::class]
)
interface ApplicationComponent : AndroidInjector<MyApp> {

    override fun inject(instance: MyApp?)

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application): Builder
        fun build(): ApplicationComponent
    }
}

ActivityBindingModule:

@Module
abstract class ActivityBindingModule {

    ...

    @ActivityScoped
    @ContributesAndroidInjector(modules = [ActivityInQuestionModule::class])
    internal abstract fun aiqActivity(): ActivityInQuestion

    @ActivityScoped
    @Binds
    internal abstract fun fragmentSwitcher(activityInQuestion: ActivityInquestion): FragmentSwitcher

}

ActivityInQuestionModule:

@Module
abstract class ActivityInQuestionModule {

    @FragmentScoped
    @ContributesAndroidInjector
    internal abstract fun fragmentOne(): FragmentOne

    @FragmentScoped
    @ContributesAndroidInjector
    internal abstract fun fragmentTwo(): FragmentTwo

    @FragmentScoped
    @ContributesAndroidInjector
    internal abstract fun fragmentThree(): FragmentThree

}

Solution

  • I'm pretty sure that dagger is not the reason of leak (if it's existed). What dagger do is just instantiating members of activity with appropriate instances. So you can try to run LeakCarnary with code modified like this: lateinit var fragmentOne = FragmentOne() ... and verify if memory leak exists or not. Maybe the problem is in your fragments code.