Search code examples
androiddependency-injectiondagger-2dagger

Dagger why to create custom scope


I'm learning dagger and I fount this article that explains how to use android.dagger everything is clear for me except with custom created scopes. Previously I saw a lot of tutorials where custom scope is created to create dependencies to specific situations (e.g Logged In scope). But that tutorial showed my other approach. Here's my example: I have a class that should be generated only for MainActivity (and MasterActivity) but not for LoginActivity

class SecretKey(
    val token: String,
    val userId: Long
)

So here's the module

@Module
class MainActivityModule {
    @Provides
    fun provideSecretKey(preference: SharedPreferences): SecretKey {
        return SecretKey(
            "jwtToken",
            465465
        )
    }
}

and ActivitiesBindingModule

@Module
abstract class ActivitiesBindingModule {
    @ContributesAndroidInjector(modules = [MainActivityModule::class])
    abstract fun mainActivity(): MainActivity

    @ContributesAndroidInjector(modules = [LoginActivityModule::class])
    abstract fun loginactivity(): LoginActivity

    // MasterActivity will see everything from MainActivityModule and LoginActivityModule
    @ContributesAndroidInjector(modules = [MainActivityModule::class, LoginActivityModule::class])
    abstract fun masterActivity(): MasterActivity
}

So as I understood only in MainActivity and MasterActivity I will be able to inject SecretKey class because of the ContributesAndroidInjector module. So SecretKey scope is within MainActivity and MasterActivity. So why we still are able to create custom scopes with Scope annotation? Is this alternative?


Solution

  • Scope simply tells Dagger to save the instance of the scope-annotated object rather than creating a new one. Dagger will save the instance in the component of the corresponding scope. (You should also know that @ContributesAndroidInjector code-generates a Subcomponent instance for you, so if you annotate the @ContributesAndroidInjector method with a scope annotation, the generated subcomponent will take that scope.)

    In your example, SecretKey is not scoped; every time you ask Dagger to inject a SecretKey it will call your constructor and create a brand new instance. This is probably fine, as SecretKey seems not to keep state, and keeping the instance scopeless allows the garbage collector to collect SecretKey when you no longer need it.

    However, imagine that you create your own Cache object, and that you want that Cache to live as long as the activity but not longer: Each new Activity instance should get its own Cache. Unlike SecretKey, you could not create your own Cache every time one is requested; you'd need to save your cache instance somewhere. You could choose to do this as a field on the Activity itself, or you could make a Module instance that saves an instance value the first time you call a @Provides method, but Dagger prefers that you mark the binding with a scope annotation that matches the component. This allows you to declare and document that the binding will have the same lifetime as the component, and makes it easy to categorize bindings as "Application-scoped", "Activity-scoped", "Fragment-scoped", "Service-scoped", and so forth.