Skip to content

Dependency auto scoping

Sergej Shafarenka edited this page May 14, 2018 · 5 revisions

This article explains how to create hierarchical scopes and how Magnet creates and binds instances into those scopes.

  1. Create the root scope for your objects inside of you Application. Created scope has same lifespan as your application.
val appScope = Magnet.createRootScope()
  1. Inside of your Activity create another scope with a shorter lifespan, corresponding to the lifespan of your activity. Lets create and initialize it with an instance of Resources which will reside inside this scope.
val activityScope = App.appScope.createSubscope {
    bind(resources)
}

Created scope holds a reference to its parent scope which is appScope.

  1. Lets create a third empty scope with a Fragment lifespan.
val fragmentScope = activityScope.createSubscope()

fragmentScope holds reference to activityScope. Any object created in fragmentScope can depend on the objects registered in the same scope or in any parent scope up to the root scope, meaning in either activityScope or appScope.

Our scope setup look like this

appScope ()
     ^
     |
activityScope (resources)
     ^
     |
fragmentScope ()

appScope and fragmentScope are empty and activityScope has a single Resources object in it.

  1. Let's imagine we need to create following instances with dependencies between them.
FragmentPresenter -> ItemRepository -> ItemDataSource -> Resources

First we need to declare them using @Instance annotation.

@Instance(type = FragmentPresenter::class, scoping = Scoping.DIRECT)
class FragmentPresenter(
    private val itemRepository: ItemRepository
)

@Instance(type = ItemRepository::class)
class ItemRepository(
    private val dataSource: ItemsDataSource
)

@Instance(type = ItemDataSource::class)
class ItemDataSource(
    private val resources: Resources
)

Magnet parses constructors of annotated classes, generates class factories and handles dependencies between them.

  1. Now we can ask Magnet to provide instance of FragmentPresenter class from fragmentScope like following.
val fragmentPresenter = fragmentScope.getSingle<FragmentPresenter>()

Magnet will resolve dependencies, create objects and bind them into our scopes automatically as following.

appScope ()
     ^
     |
activityScope (itemRepository, itemDataSource, resources)
     ^
     |
fragmentScope (fragmentPresenter)

Here is the explanation of how Magnet managed auto-scoping of objects.

a) fragmentPresenter was bound to fragmentScope because of scoping = Scoping.DIRECT directive forcing Magnet to register objects in the same scope, in which they were requested. Magnet did it because we called getSingle() at fragmentScope.

b) itemDataSource was bound to activityScope because this is the top most scope where its dependency (resources) is available. This is controlled by scoping = Scoping.TOPMOST directive, which is default scoping value. If resources would reside inside appScope, then Magnet would put itemDataSource into appScope too.

c) itemRepository was bound to activityScope because its dependency itemDataSource resides inside activityScope. Same as the case (b).

Auto-scoping and dependency inversion are some of the concepts differentiating Magnet from the other injection frameworks out there. Those concepts are new and they have to be validated by using them in smaller projects first. Feel free to check Magnet and share your opinion and ideas.

Clone this wiki locally