Давайте усложним создание объектов с точки зрения даггера. Добавим использование объекта Context.

 

 

 

Представим, что мы поменяли реализацию БД, и теперь нам нужен Context, чтобы создать объект Database:

Database (:data)

class Database(private val context: Context) {
}

 

Вносим соответствующие изменения в DataModule, в метод который создает Database:

DataModule (:data)

@Module
class DataModule {

   @Provides
   fun provideDatabase(context: Context) =
       Database(context)

}

Этот метод теперь ожидает, что ему передадут Context.

Как мы помним, метод provideDatabase вызывается компонентом. Значит компонент должен где-то взять Context, чтобы создать DataModule. Сам он создать такой объект не сможет, поэтому нам надо будет ему помочь.

 

 

Dagger

В даггере мы в таком случае можем добавить BindsInstance параметр для билдера или фабрики компонента.

Давайте сделаем это, иначе наш код не будет компилироваться. Даггер ведь не в курсе, что мы не используем его компонент, и все еще генерирует класс DaggerAppComponent. Если он не будет знать, откуда брать Context, то он будет выдавать ошибки при компиляции.

 

Добавляем фабрику, которая умеет получать Context

AppComponent (:app)

@Component(modules = [DataModule::class])
interface AppComponent {

   fun injectTasksActivity(tasksActivity: TasksActivity)

   @Component.Factory
   interface AppComponentFactory {
       fun create(@BindsInstance context: Context): AppComponent
   }

}

В последующих примерах кода AppComponent я не буду указывать эту фабрику

 

Создание компонента даггера теперь выглядит так:

App (:app)

class App: Application() {

   lateinit var appComponent: AppComponent

   override fun onCreate() {
       super.onCreate()
       appComponent = DaggerAppComponent.factory().create(this)
       // appComponent = MyAppComponent()
   }
}

Мы передаем фабрике App класс в качестве Context.

 

 

Наш компонент

У нас есть свой компонент. Вернемся к нему:

MyAppComponent (:app)

Ему тоже нужен Context, чтобы создавать Database

 

Давайте просто передавать Context в конструктор компонента:

MyAppComponent (:app)

class MyAppComponent(private val context: Context): AppComponent {

   private val dataModule = DataModule()

   override fun injectTasksActivity(tasksActivity: TasksActivity) {
       tasksActivity.database = dataModule.provideDatabase(context)
   }

}

Теперь компонент может использовать Context при создании объектов.

 

Создание нашего компонента выглядит так:

App (:app)

class App: Application() {

   lateinit var appComponent: AppComponent

   override fun onCreate() {
       super.onCreate()
       // appComponent = DaggerAppComponent.factory().create(this)
       appComponent = MyAppComponent(this)
   }
}

Мы передаем ему App класс, как Context.

 

 

Схема

Обновим схему

Она не особо усложнилась. Новых модулей не добавилось. Появился новый объект App, который используется компонентом в качестве Context.

 

Но тут есть один интересный момент, на который важно обратить внимание.

Компонент создает Database и в качестве контекста передает ему объект App. Т.е. объект Database из модуля data использует объект App из модуля app.

Но ведь зависимость между этими модулями прописана в другую сторону. Это классы из модуля app видят и могут использовать классы из модуля data, но не наоборот.

Магия? Почти)

 

Смотрим еще раз на код Database:

Database (:data)

class Database(private val context: Context) {
}

Видим, что на самом деле Database не знает и не использует класс App из модуля app. Он использует класс Context. Это класс из системной библиотеки, и его знают все Android модули в проекте. Поэтому все в порядке, никакие зависимости не нарушены.

Секрет в том, что Database думает, что использует объект Context, и даже не подозревает, что на самом деле использует объект App из модуля app.

 

Тут важно отличать объект от класса. Database не может использовать класс App, но может получить объект App, замаскированный под Context. Другое дело, что этот объект App будет ограничен рамками Context. У Database не получится привести его к классу App, и получить от него, например, App компонент. Потому что Database не знает класс App, как впрочем и класс AppComponent.

Эта маскировка является важной частью мультимодульной темы. В последующих примерах мы увидим, как для этих же целей можно использовать интерфейсы.

 

 

Результат

Мы добавили использование Context в классе Database. Для этого нам пришлось предоставить компоненту объект Context, потому что сам он создать такой объект не может. В качестве Context мы использовали объект App. Это привело к тому, что объект из модуля data использовал объект из модуля app, несмотря на то, что зависимость прописана в обратную сторону. В итоге мы выяснили, что объекты одного модуля могут использовать объекты другого модуля, даже если нет необходимой зависимости. Главное, чтобы объект был представлен в виде знакомого класса.


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме 




Language

Автор сайта

Дмитрий Виноградов

Подробнее можно посмотреть или почитать.

Никакие другие люди не имеют к этому сайту никакого отношения и просто занимаются плагиатом.

Социальные сети

 

В канале я публикую ссылки на интересные и полезные статьи по Android

В чате можно обсудить вопросы и проблемы, возникающие при разработке



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal