Мы решаем реализовать в приложении работу не только с БД, но и с сервером. Поэтому добавляем новый (и последний) модуль - network.

 

 

 

Пока что этот новый модуль не даст нам никаких новых знаний с точки зрения даггера и мультимодульности. Он будет похож на модуль data по своим связям с app и task. Но в следующих уроках разница станет видна.

 

В новом модуле создаем API интерфейс:

TaskApi (:network)

interface TaskApi {
}

 

и даггер-модуль:

NetworkModule (:network)

@Module
class NetworkModule {

   @Provides
   fun provideTasksApi(): TaskApi {
       return object : TaskApi { }
   }

}

 

Т.к. мы хотим использовать это API в модуле task, то добавляем зависимость task от network

build.gradle (:task)

implementation project(path: ':network')

 

 

Давайте заодно создадим репозиторий, который будет совмещать использование БД и API:

TaskRepository (:task)

class TaskRepository(
   private val database: Database,
   private val taskApi: TaskApi
) {
  
}

 


Создаем даггер-модуль, который умеет создавать репозиторий:

TaskModule (:task)

@Module
class TaskModule {

   @Provides
   fun provideTaskRepository(database: Database, taskApi: TaskApi): TaskRepository {
       return TaskRepository(database, taskApi)
   }
  
}

 

Во фрагмент вместо БД будем инджектить репозиторий:

TaskFragment (:task)

class TasksFragment : Fragment() {

   @Inject
   lateinit var taskRepository: TaskRepository

 

 

Чтобы компонент в модуле app смог собрать репозиторий, ему нужны классы:
- TaskRepository из task
- Database из data
- FileManager из core
- TaskApi из network

Про объекты из модулей task, data и core он уже знает. Надо добавить зависимость app от network:

build.gradle (:app)

implementation project(path: ':network')

 

Схема модулей теперь выглядит так:

 

Добавляем в компонент даггер-модули: NetworkModule и TaskModule

AppComponent (:app)

@Component(modules = [DataModule::class, CoreModule::class, NetworkModule::class, TaskModule::class])
interface AppComponent: TaskComponent {
   override fun injectTasksFragment(tasksFragment: TasksFragment)
}

Теперь даггер сможет создать компонент, который использует модули для создания репозитория и всех необходимых для этого объектов.

 

Мы со своей стороны тоже подправим нашу реализацию компонента, чтобы примерно понимать, что делает даггер:

MyAppComponent (:app)

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

   private val dataModule = DataModule()
   private val coreModule = CoreModule()
   private val networkModule = NetworkModule()
   private val taskModule = TaskModule()


   override fun injectTasksFragment(tasksFragment: TasksFragment) {
       tasksFragment.taskRepository = taskModule.provideTaskRepository(
           dataModule.provideDatabase(context, coreModule.provideFileManager()),
           networkModule.provideTasksApi()
       )
   }

}

Создаем даггер-модули и с их помощью собираем репозиторий

 

Схема объектов:

 

Выделим основные моменты

Фрагмент использует репозиторий. А репозиторий использует TaskApi и Database. Поэтому модулю task нужны модули data и network. FileManager напрямую в репозитории не используется, поэтому модуль core не нужен

 

 

Если фрагмент просто использует репозиторий, то компоненту его приходится собирать. Для этого ему нужны TaskApi, Database и FileManager. Поэтому app зависит от data, network и core.

 

 

 Фрагмент использует компонент, чтобы получить собранный объект репозитория.

 

 

Результат

На этом мы закончили добавлять модули и объекты. Далее займемся конфигурацией компонента.

Сейчас у нас используется AppComponent, который находится в модуле app. Он собирает объекты для фрагмента в модуле task.

Что дает нам такая конфигурация в случае, когда у нас много модулей, похожих на модуль task, со своими фрагментами, которые используют объекты из модулей data, core, network и т.п.?

Давайте выделим основные моменты

1) Scope
Компонент AppComponent предоставляет нам только Application scope. С ним не получится создать для объекта синглтон, время жизни которого находится в рамках какого-либо экрана. Этот синглтон будет жить все время работы приложения.
Это определенно минус текущей конфигурации. Хотелось бы иметь более гибкие scope.

2) Зависимости модулей
Модуль app должен знать абсолютно все модули, потому что компонент собирает все объекты для всех фрагментов.
Тоже можно считать минусом, особенно если модулей много.

3) Зона ответственности компонента
Компонент знает очень много. Он умеет инджектить в каждый фрагмент. Слишком большая ответственность для одного объекта.
Это тоже не очень хорошо. Такую ответственность надо бы раскидать по разным классам.

4) Размер класса компонента
Сгенерированный класс компонента получится огромным.
Некритично, но тоже неприятно.

 

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

У нас два варианта: сделать этот компонент саб-компонентом для AppComponent или отдельным компонентом (не саб). Рассмотрим оба варианта.


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

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

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

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




Language

Автор сайта

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

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

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

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

 

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

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



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



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

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal