10

I'm trying to understand the use of the @Reusable scope of Dagger. From the documentations what I can understand is that if a provider is scoped with @Singleton or any other custom scope, then the object will be created first and then cached for the entire lifetime of the component. So, for objects which don't always need to be the same instance or which are less often used, this approach will end up in wasting memory.

But if we go for a non-scoped provider, each time it will create a new instance, and since object instantiation is expensive especially in environments such as Android, where allocations can be expensive, this may cause performance issues.

@Reusable scope lies somewhere between No-scope and Scoped instances. From the documentation

Sometimes you want to limit the number of times an @Inject-constructed class is instantiated or a @Provides method is called, but you don’t need to guarantee that the exact same instance is used during the lifetime of any particular component or subcomponent

How does it work exactly? Suppose I have a reusable provider in my AppComponent, won't it always give me the same instance?

If I inject the same dependency in any Subcomponent, will I be getting the same instance? When exactly will the cached object be freed up for GC?

I tried with a sample, creating a @Reusable object in my AppComponent module and injecting the same from my subcomponents.
What I can see is that it is behaving exactly as @Singleton.

What performance improvement can we achieve from @Reusable?

Which are the possible usecases where we should prefer @Reusable?

Is it a good idea to scope all the state-less objects (where it doesn't matter whether we get the same instances) such as Util classes, Gson, Glide, etc. as @Reusable?

David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • Marking this as a duplicate of [Dagger @Reusable scope vs @Singleton](https://stackoverflow.com/questions/39136042/dagger-reusable-scope-vs-singleton): I think it answers most of your sub-questions, but may not answer them all. Let me know if you'd rather keep this question open, but I think it makes sense to combine the questions/answers. – Jeff Bowman Dec 11 '17 at 18:07

1 Answers1

12

I'd like to point out that @Reusable as of v2.13 is still in beta, so it may be changed or removed again.

tl;dr It behaves like a variable scope without the guarantee of having a single instance at any given time.

How does it work exactly? Suppose I have a reusable provider in my AppComponent, won't it always give me the same instance?

The Javadoc is quite (un)clear on that subject:

A scope that indicates that the object returned by a binding may be (but might not be) reused.

@Reusable is useful when you want to limit the number of provisions of a type, but there is no specific lifetime over which there must be only one instance.

It does not give any information on how it works under the hood and you should not rely on any potential implementation as they might change, especially while it is still in @Beta.

The assumption is that @Reusable can be used for objects that can exist multiple times, but where the instantiation of multiple objects might be a bit more costly so that it would make sense to reuse the object instead.

While the implementation might change, the intended use will not. So if you decide to use @Reusable you should make sure that it won't matter whether you have one, two, or multiple instances of your object, where or if they get cached.

Which are the possible usecases where we should prefer @Reusable?

Is it a good idea to scope all the state-less objects (where it doesn't matter whether we get the same instances) such as Util classes, Gson, Glide, etc. as @Reusable?

As mentioned, you should use it would make sense for an object to be reused, as the name would suggest. Gson is a rather bad example, since it uses quite a bit of reflection and the instantiation of it is quite costly, it should probably be @Singleton. Glide is not a good example either, as it will use a Singleton pattern internally anyways.

One benefit of @Reusable over @Singleton is that you don't make a claim about the hierarchy between scopes or components. Scoping an object as @Singleton will mean that your AppComponent will hold the object over its entire lifetime, whereas with @Reusable the object might only be created in a child component all the way down the dependency tree and destroyed again along with it.
I would not use it for objects with very few dependencies, as they can be created quite easily, but use it for objects that don't hold any state and require a bit more setup.

But how does it work?

Reusable is a kind of @Scope that gets some special treatment. You can see the commit where it was added.

As a scope, objects annotated with @Reusable will be held within a component. You can have a look at the first unit test. It verifies that a child component will re-use their parent's provider, if available. This is the behavior you mentioned and why it seems to make no difference to @Singleton.

The difference to a normal scope lies in the Provider used. Instead of using a ScopedProvider, @Reusable uses a SimpleLazilyInitializedProvider. It omits the synchronized keyword when creating the object, which might give a slight boost to performance, but explains why there is no specific lifetime over which there must be only one instance.

I would not be surprised if they changed the inner workings of @Reusable in the future, but for now knowing that it acts scope-ish might help to decide when to use it.

Community
  • 1
  • 1
David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • Thanks for the nice detailed explanation!! –  Dec 12 '17 at 10:13
  • For today Dagger uses SingleCheck for Reusable providing. Before I thought that if app component provides reusable items and after some usage and if there are no actual references it just cleans them (e.g. WeakReference). But now I see that Dagger caches the items and doesn't clean them never. It means that provided item with Reusable annotation will be cached until Dagger component will be destroyed. If you need a behavior with caching until a component is used in different places you can provide item without any scope and cache it inside of Module. – ultraon Dec 19 '18 at 23:46