Skip to content

Dagger 2 Scopes Explained and the mighty Singleton (Scope)

Dagger 2 Scopes are something that a lot of people don’t understand. Many people use them without understanding why they’re used, simply because they modelled their app after a Dagger tutorial and things just worked out. I myself am guilty of this.

So in this article, I’m going to go over what scopes are, what they look like in code, what their purpose is in the application and why we want to make use of them.

What are Scopes

Think back to the Singleton annotation. Singleton allows you to have one instance of an object running across the whole application, and every time it is provided to a different class, the same instance of that object is injected.

This is great for a number of reasons but imagine a scenario where we just have so many different singletons running in our app. That would quickly result in a lot of overhead and generally make our app slow.

Singleton is an app-wide scope. It defines how long we want to keep annotated instances alive, which in the case of Singleton, would be the lifetime of the app running.

We can define custom scopes and what that does is tells instances of an object annotated with that scope that “Hey, I want to keep you alive only for as long as any of these components are alive”.

Scopes in Code

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class FeaturesActivityScope

Creating a custom scope is as simple as creating an annotation class as simple as this one. The RUNTIME annotation simply tells us we want to keep provided dependencies in this scope alive for as long as the component (Activity, etc.) of this scope is alive.

@FeaturesActivityScope
@Component(modules = [FeaturesActivityModule::class], dependencies = [AppComponent::class])
interface FeaturesActivityComponent {

    fun inject(featuresHomepageActivity: FeaturesHomepageActivity)

}

So I annotate my component with my custom scope to tie the scope to the component’s lifetime.

@Module
class FeaturesActivityModule(private val featuresHomepageActivity: FeaturesHomepageActivity) {

    @Provides
    @FeaturesActivityScope
    fun view(featuresHomepageViewModel: FeaturesHomepageViewModel): FeaturesHomepageFragment {
        return FeaturesHomepageFragment(featuresHomepageViewModel)
    }

    @Provides
    @FeaturesActivityScope
    fun viewModel(featuresHomepageModel: FeaturesHomepageModel): FeaturesHomepageViewModel {
        return FeaturesHomepageViewModel(featuresHomepageModel)
    }

}

Thus the instances of everything provided in the module will be kept alive only for as long as my component stays alive. The same one instance of each object will be provided during this duration, but only for the full lifetime of my component. Once the component dies, these instances die.

Why do we want to use Scopes?

Dagger is a very complicated tool in itself and the concept of scopes might sound like we’re just adding another layer of complexity, so why do we want to use Scopes in our app?

Well, the answer is we don’t want to keep instances running when they don’t need to be. While the repeated creation of instances of an object can result in a good amount of overhead, so can having 500 instances of different objects running in the background simultaneously, despite the fact you need only 10 of them.

Scopes allow us to achieve the balance of having instances of objects running for only as long as we need them. If your app has been under development for any decent amount of time, you’ll find that you have more than a few activities. If every dependency needed in every single activity is running together, but many of them are only needed in one or two of them, your app will slow down to a crawl for very unnecessary reasons.

Using scopes will allow you to properly dispose of dependencies when they’re not being used, while providing that sort-of global point of access during components’ lifetimes which is why we use annotations like Singleton in the first place.

Conclusion

No longer must you live ignorant of the power of scopes. Despite being overlooked a ton by many developers, they really aren’t a hard concept to understand.

Especially when we so easily grasp the concept of Singleton which is in itself, an app-wide scope. Custom scopes are just Singletons with shorter lifespans (in a way I guess).

In any case, I hope that gets us one step into understanding the lovable beast that is Dagger. Happy coding ༼ つ ◕_◕ ༽つ

 

Tags: