Предыдущая часть
Содержание
Внедрение методов и полей
В первой части описан метод внедрение зависимости на уровне конструктора. Кроме этого, dagger может внедрять зависимости для полей и методов. Но эти внедрения следует использовать при крайней необходимости.
Пример внедрения метода:
На примере выше внедряется зависимость метода set для поля key
Внедрение полей происходит в три этапа:
Наглядно будет проще понять это, по порядку
1. В наш абстрактный класс добавим метод внедрения для MainActivity
2. Определим поля которые должны быть внедрены в MainActivity. Внедряемые поля должны быть lateinit var и видны всем (public)
3. Вызываем добавленным нами метод inject() абстрактного класса для внедрения полей активити.
В конечном итоге наш класс MainActivity будет выглядеть след. образом:
Отложенная инициализация в dagger
Как известно при запуске приложения не всегда нужны экземпляры всех классов. Это ускоряет первый запуск и не только. В dagger есть 2 вида инициализации внедряемых объектов: Provider<> И Lazy<>
Provider – инициализация происходит при первом обращении к объекту и при каждом вызове будет возвращен новый экземпляр объекта
Lazy – инициализация происходит при первом обращении, далее возвращается ранее кешированный экземпляры
Для применения данных видов инициализации необходимо «обернуть» инициализируемые объекты в нужный вид.
Пример использования Provider:
При каждом вызове метода get() получаем новый экземпляр нужного объекта.
Пример использования Lazy:
При каждом вызове метода get() получаем один и тот же экземпляр.
Внимание! При добавлении метода get() у вида Lazy в Android Studio метод может быть подчеркнут красным т.к. у Kotlin есть свой класс Lazy. По этому импортируем класс dagger
Модули dagger. Когда dagger вас не понимает
Бывают такие случаи когда dagger не понимает ваши намерения. На пример у нашего класса Car есть поле типа (интерфейс) Driver, который наследуется классом Ivanov
При попытке внедрить поле с типом интерфейс вы получите ошибку «cannot be provided without an @Provides-annotated methods».
Для решения этой проблемы dagger предлагает использовать Модули. Модули обеспечивают dagger дополнительной информацией которые он не может получить самостоятельно. В качестве модули можно использовать интерфейсы или объекты (object).
Для решения задачи выше создадим модуль:
В методе bindDriver мы объясняем dagger как необходимо инициализировать интерфейс.
Так же в компоненте нужно перечислить все существующие модули dagger
Предположим для нашего класса Engine используется поле сторонней библиотеки cylinder (интерфейс). Как описать такое поле для dagger если не понятно какой класс будет инициализирован в runtime?
До сих пор мы использовали аннотацию для того, чтоб объяснить dagger как нужно внедрять зависимости. Как быть если вы не знаете, как нужно создавать классы, из чужих библиотек, на пример?
Аннотация Provides описывает те случаи когда нужно явно описать экземпляр какого класса нужно инициализировать.
Тем самым мы говорим dagger что при инициализации поля cylinder нужен экземпляр класса BoschCylinder.
Аннотация Named. Несколько экземпляра одно типа
Бывают случаи, когда нужно создать экземпляры одного класса с разными настройками. В нашем примере это разные цвета на кузове и дверей.
При попытке построить проект со след. Модулем получим ошибку «(наш класс) Color is bound multiple times»
Для решения таких случаев используется аннотация Named. В первую очередь в модуле создадим 3 новых метода для инициализации в dagger
Первый метод по умолчанию, без него dagger будет ругаться об отсутствии класса «cannot be provided without an Inject constructor or an Provides-annotated method»
Следующие два метода возвращающие экземпляры одного и того же класса. Осталось добавить внедрение этого класса в нужных местах и вызвать с аннотацией Named
Исходник
Содержание
- Внедрение методов и полей
- Отложенная инициализация в dagger
- Модули dagger. Когда dagger вас не понимает
- Аннотация Named. Несколько экземпляра одного типа
Внедрение методов и полей
В первой части описан метод внедрение зависимости на уровне конструктора. Кроме этого, dagger может внедрять зависимости для полей и методов. Но эти внедрения следует использовать при крайней необходимости.
Пример внедрения метода:
class Car @Inject constructor(private var engine: Engine){
var key: Key? = null
@Inject set
}
class Key @Inject constructor()
На примере выше внедряется зависимость метода set для поля key
Внедрение полей происходит в три этапа:
- Добавить метод внедрения в абстрактную фабрику
- Определить поля которые будут внедрены
- Использовать методы внедрения в имплементации dagger абстрактного класса для внедрения зависимостей
Наглядно будет проще понять это, по порядку
1. В наш абстрактный класс добавим метод внедрения для MainActivity
@Component
interface DaggerComponent {
fun getCar(): Car
fun getEngine(): Engine
fun getFuel(): Fuel
fun inject(act: MainActivity)
}
2. Определим поля которые должны быть внедрены в MainActivity. Внедряемые поля должны быть lateinit var и видны всем (public)
@Injected
lateinit var car: Car
3. Вызываем добавленным нами метод inject() абстрактного класса для внедрения полей активити.
В конечном итоге наш класс MainActivity будет выглядеть след. образом:
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)
}
}
Отложенная инициализация в dagger
Как известно при запуске приложения не всегда нужны экземпляры всех классов. Это ускоряет первый запуск и не только. В dagger есть 2 вида инициализации внедряемых объектов: Provider<> И Lazy<>
Provider – инициализация происходит при первом обращении к объекту и при каждом вызове будет возвращен новый экземпляр объекта
Lazy – инициализация происходит при первом обращении, далее возвращается ранее кешированный экземпляры
Для применения данных видов инициализации необходимо «обернуть» инициализируемые объекты в нужный вид.
Пример использования Provider:
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()
При каждом вызове метода get() получаем новый экземпляр нужного объекта.
Пример использования 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!")
}
}
}
При каждом вызове метода get() получаем один и тот же экземпляр.
Внимание! При добавлении метода get() у вида Lazy в Android Studio метод может быть подчеркнут красным т.к. у Kotlin есть свой класс Lazy. По этому импортируем класс dagger
import dagger.Lazy
Модули dagger. Когда dagger вас не понимает
Бывают такие случаи когда dagger не понимает ваши намерения. На пример у нашего класса Car есть поле типа (интерфейс) Driver, который наследуется классом Ivanov
При попытке внедрить поле с типом интерфейс вы получите ошибку «cannot be provided without an @Provides-annotated methods».
Для решения этой проблемы dagger предлагает использовать Модули. Модули обеспечивают dagger дополнительной информацией которые он не может получить самостоятельно. В качестве модули можно использовать интерфейсы или объекты (object).
Для решения задачи выше создадим модуль:
@Module
interface DaggerModul {
@Binds
fun bindDriver(driver: Ivanov): Driver
}
class Ivanov @Inject constructor(): Driver
В методе bindDriver мы объясняем dagger как необходимо инициализировать интерфейс.
Так же в компоненте нужно перечислить все существующие модули dagger
@Component(modules = [DaggerModul::class])
interface DaggerComponent {
…
}
Предположим для нашего класса Engine используется поле сторонней библиотеки cylinder (интерфейс). Как описать такое поле для dagger если не понятно какой класс будет инициализирован в runtime?
До сих пор мы использовали аннотацию для того, чтоб объяснить dagger как нужно внедрять зависимости. Как быть если вы не знаете, как нужно создавать классы, из чужих библиотек, на пример?
Аннотация Provides описывает те случаи когда нужно явно описать экземпляр какого класса нужно инициализировать.
@Module
object DaggerModuleObject {
@Provides
@JvmStatic
fun getBoschCylinder(): Cylinder = BoschCylinder()
}
Тем самым мы говорим dagger что при инициализации поля cylinder нужен экземпляр класса BoschCylinder.
Аннотация Named. Несколько экземпляра одно типа
Бывают случаи, когда нужно создать экземпляры одного класса с разными настройками. В нашем примере это разные цвета на кузове и дверей.
При попытке построить проект со след. Модулем получим ошибку «(наш класс) Color is bound multiple times»
@Provides
@JvmStatic
fun getColorRed():Color = Color("red")
@Provides
@JvmStatic
fun getColorBlue():Color = Color("blue")
Для решения таких случаев используется аннотация Named. В первую очередь в модуле создадим 3 новых метода для инициализации в 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")
Первый метод по умолчанию, без него dagger будет ругаться об отсутствии класса «cannot be provided without an Inject constructor or an Provides-annotated method»
Следующие два метода возвращающие экземпляры одного и того же класса. Осталось добавить внедрение этого класса в нужных местах и вызвать с аннотацией 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()
Исходник