Dagger 2 is Elementary (Part 2)

Previous part



Content



  1. Implement methods and fields
  2. Delayed initialization in dagger
  3. Dagger modules. When the dagger doesn't understand you
  4. Abstract Named. Multiple instances of the same type


Implement methods and fields



The first part describes a method for implementing dependency at the constructor level. In addition, dagger can implement dependencies for fields and methods. But these implementations should be used when absolutely necessary.



Method implementation example:



class Car @Inject constructor(private var engine: Engine){ var key: Key? = null @Inject set } class Key @Inject constructor()
      
      





In the example above, the set method dependency is implemented for the key field



The implementation of the fields takes place in three stages:



  1. Add embed method to abstract factory
  2. Define fields to be implemented
  3. Use embed methods in an abstract class dagger implementation to implement dependencies


It will clearly be easier to understand this, in order



1. In our abstract class, add an implementation method for MainActivity



 @Component interface DaggerComponent { fun getCar(): Car fun getEngine(): Engine fun getFuel(): Fuel fun inject(act: MainActivity) }
      
      





2. Define the fields that should be implemented in MainActivity. Injected fields should be lateinit var and visible to everyone (public)



 @Injected lateinit var car: Car
      
      





3. We invoke the inject () method of the abstract class to implement the activity fields that we added.



Ultimately, our MainActivity class will look like a trace. way:



 class MainActivity : AppCompatActivity() { @Inject lateinit var car: Car override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) DaggerDaggerComponent.create().inject(this) } }
      
      





Delayed initialization in dagger



As you know, when starting an application, instances of all classes are not always needed. This speeds up the first launch and more. There are 2 types of initialization of embedded objects in dagger: Provider <> And Lazy <>



Provider - initialization occurs on the first call to the object and with each call a new instance of the object will be returned

Lazy - initialization occurs on the first call, then previously cached instances are returned



To use these types of initialization, it is necessary to "wrap" the initialized objects in the desired form.



Provider Usage Example:



 class Engine @Inject constructor(private var fuel: Fuel){ fun start(){ if(fuel!=null){ print("Started!") }else{ print("No more fuel!") } } } class Car @Inject constructor(private var engine: Provider<Engine>){ var key: Key? = null @Inject set fun startCar(){ engine.get().start() } } class Key @Inject constructor()
      
      





Each time the get () method is called, we get a new instance of the desired object.



An example of using Lazy:



 class Fuel @Inject constructor() { val fuelType = if(BuildConfig.DEBUG){ "benzine" }else{ "diesel" } } class Engine @Inject constructor(private var fuel: Lazy<Fuel>){ fun start(){ if(fuel!=null){ print("Started with ${fuel.get().fuelType}") }else{ print("No more fuel!") } } }
      
      





Each time the get () method is called, we get the same instance.



Attention! When adding the get () method of the Lazy view in Android Studio, the method may be underlined in red since Kotlin has its own Lazy class. So we import the dagger class



 import dagger.Lazy
      
      





Dagger modules. When the dagger doesn't understand you



There are times when a dagger does not understand your intentions. For example, our Car class has a field of type (interface) Driver, which is inherited by the Ivanov class



When you try to implement a field with an interface type, you get the error “cannot be provided without an @ Provides-annotated methods”.



To solve this problem, dagger suggests using Modules. The modules provide the dagger with additional information that it cannot obtain on its own. As modules, you can use interfaces or objects (object).



To solve the problem above, create a module:



 @Module interface DaggerModul { @Binds fun bindDriver(driver: Ivanov): Driver } class Ivanov @Inject constructor(): Driver
      
      





In the bindDriver method, we explain to dagger how to initialize the interface.



Also in the component you need to list all existing dagger modules



 @Component(modules = [DaggerModul::class]) interface DaggerComponent { … }
      
      





Suppose the third-party library field (interface) is used for our Engine class. How to describe such a field for dagger if it is not clear which class will be initialized in runtime?



So far, we have used annotation to explain to dagger how dependencies should be injected. What if you do not know how to create classes from foreign libraries, for example?



Provides annotation describes those cases when you need to explicitly describe an instance of which class you want to initialize.



 @Module object DaggerModuleObject { @Provides @JvmStatic fun getBoschCylinder(): Cylinder = BoschCylinder() }
      
      





Thus, we tell dagger that when initializing the cylinder field, an instance of the BoschCylinder class is needed.



Abstract Named . Multiple instances of the same type



There are times when you need to create instances of the same class with different settings. In our example, these are different colors on the body and doors.



When trying to build a project with a trace. The module will get the error "(our class) Color is bound multiple times"



 @Provides @JvmStatic fun getColorRed():Color = Color("red") @Provides @JvmStatic fun getColorBlue():Color = Color("blue")
      
      





To solve such cases, the Named annotation is used. First of all, in the module we will create 3 new methods for initialization in dagger



 @JvmStatic @Provides fun getColor(): Color = Color("") @Provides @Named("blueColor") @JvmStatic fun getColorBlue(): Color{ return Color("blue") } @JvmStatic @Named("redColor") @Provides fun getColorRed(): Color = Color("red")
      
      





The first method is the default, without it the dagger will swear about the absence of the class “cannot be provided without an Inject constructor or an Provides-annotated method”



The following two methods return instances of the same class. It remains to add the implementation of this class in the right places and call with the annotation Named



 class Door @Inject constructor() { @Named("blueColor") @Inject lateinit var color:Color } class Car @Inject constructor(private var engine: Provider<Engine>, private var door: Door, var driver: Driver){ var key: Key? = null @Inject set @Inject @Named("redColor") lateinit var color: Color fun startCar(){ engine.get().start() } } class Key @Inject constructor()
      
      





Source code



All Articles