Изначально я планировал в рамках своего курса Dagger написать урок про использование даггера в мультимодульном проекте. Но быстро понял, что одним уроком тут не обойтись. Более того, стало понятно, что материал получается большой и достаточно полезный для новичков. В итоге я решил оформить все это отдельной серией статей в открытом доступе.
Оглавление
4. Используем Context при создании объекта
6. Фрагмент в отдельный модуль
10. Заключение и полезные ссылки
Чтобы понимать, о чем пойдет речь, рекомендуется иметь базовые знания даггера. В моем курсе открыты первые несколько уроков. Это необходимый минимум. А также желательно знать про сабкомпоненты и примерно понимать, что такое scope.
О чем будем говорить
Сразу хочу прояснить, что этот материал не покажет вам правильный вариант использования даггера в проекте и разбиения проекта на модули. Просто потому, что правильных вариантов не существует. Есть разные варианты. Они зависят от личных предпочтений, используемой архитектуры, сложности проекта.
Цель этой серии статей - дать понимание, как работает даггер в мультимодульном проекте. Для начинающих разработчиков обе этих темы не являются простыми. А вместе они представляют собой комбо в виде черного ящика, в котором совершенно непонятно что происходит.
Чтобы разобраться, как оно все работает, мы по шагам будем создавать 5-модульное приложение. В одном из этих модулей (не app) создадим фрагмент, в который компонент будет инджектить объекты. В последних уроках мы рассмотрим три варианта реализации компонента:
- AppComponent, расположен в app модуле
- сабкомпонент от AppComponent, расположен в модуле фрагмента
- отдельный компонент с dependencies, расположен в модуле фрагмента
Выясним, в чем разница между этими вариантами.
Кроме этого, мы будем использовать свою реализацию компонентов вместо даггеровской, чтобы понимать, как все это работает под капотом.
Полученные знания позволят вам лучше разбираться в темах Dagger и мультимодульность. Вам будет проще читать и понимать статьи, в которых описаны рабочие варианты использования даггера в мультимодульном проекте. И это поможет вам придумать реализацию, которая подходит вашему проекту.
Чтобы лучше усвоить материал, рекомендую вам создать новый проект и выполнять все шаги. Кода будет немного. Мы будем только инджектить объекты в Activity и фрагменты. А сами классы будут пустыми и не будут ничего делать. Также здесь не будет никакой архитектуры, презентеров, ViewModel и прочего, чтобы не усложнять.
Создание проекта
Давайте переходить к практике. Будем создавать приложение Менеджер задач.
Создаем новый пустой проект со стандартным модулем app и MainActivity в нем. Для отображения списка задач мы хотим использовать отдельное Activity. Потом переделаем на фрагмент, не волнуйтесь )
Создаем TaskActivity в app модуле.
TasksActivity (:app)
class TasksActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_tasks) } }
Обратите внимание, при публикации фрагментов кода я указываю имя файла и в скобках имя модуля, в котором этот класс находится
Чтобы работать со списком задач, нам нужна база данных. И так как у нас тут урок про мультимодульность, то мы создадим отдельный модуль, где будут лежать классы для работы с данными. Пока что в этом модуле будет лежать только класс для работы с БД.
Создаем модуль data. В меню студии: File > New > New Module
Все создаваемые модули будем создавать с типом Android Library
Package name у вас будет другой
После некоторых размышлений студия должна показать новый модуль в списке слева, на одном уровне с app
В модуле data создаем класс для работы с базой данных:
Database (:data)
class Database{ }
Расположение (package) классов в модуле выбирайте любое, подходящее под вашу религию ) Сейчас это не имеет значения.
Итак, теперь у нас в проекте есть два модуля: app и data. Между ними пока нет никаких зависимостей. Они не знают друг про друга.
В этих модулях есть классы TasksActivity и Database
Класс Database мы хотим использовать в TasksActivity. Давайте пока без даггера попробуем сами создавать его там.
TasksActivity (:app)
Студия подсказывает, что Activity ничего не знает про такой класс. И просто добавить импорт тут не получится. Потому что Activity лежит в модуле app, а Database - в модуле data. И эти модули ничего друг про друга не знают.
На схеме это можно изобразить так
Я закрасил стрелку красным цветом, чтобы показать, что класс Database планирует быть использованным в TasksActivity, но пока не может.
Отдельно стоит пояснить про направление стрелок. Если объект A использует B, то некоторые рисуют стрелку от A к B, а некоторые - наоборот от B к A.
У первых стрелка имеет значение: “использует”. Т.е. стрелка от A к B означает, что A использует B.
У вторых стрелка имеет значение: “используется в”. Т.е. стрелка от B к A означает, что B используется в A.
Я буду использовать второй вариант, потому что он более подходит к теме даггера. Стрелка показывает какой объект куда будет инджектиться. Т.е. если стрелка от B к A, это значит, что даггер должен инджектить B в A.
В нашем случае стрелка от Database к TasksActivity значит, что Database используется в TasksActivity, и даггеру надо будет инджектить Database в TasksActivity.
Итак. Чтобы класс Database из модуля data можно было использовать в классе TasksActivity из модуля app, необходимо, чтобы модуль app знал про модуль data.
Для этого добавляем dependencies в build.gradle модуля app
build.gradle (:app)
implementation project(path: ':data')
и синхронизируем gradle
Теперь схема модулей выглядит так
Модуль app знает про модуль data. Классы из data теперь видны в классах из app.
После создания этой зависимости студия предложит добавить import
TasksActivity (:app)
И мы сможем использовать Database в TasksActivity
Именно для этого и нужны зависимости между модулями.
Созданный нами модуль data не стоит рассматривать как какую-то очень сложную штуку типа отдельного подпроекта в основном проекте. Это скорее похоже на продвинутый package, который ограничивает видимость своих классов.
Т.е. TasksActivity и Database - это все еще классы, находящиеся в одном проекте. Просто по умолчанию они скрыты друг от друга. Чтобы Database можно было использовать в TasksActivity, мы в build.gradle явно прописали что классы из модуля data должны быть видны классам из модуля app.
Результат
Кратко резюмируем, что у нас получилось. Мы создали простое приложение с двумя модулями app и data. В модуле app у нас TasksActivity, а в модуле data - Database. Чтобы TasksActivity могло использовать Database, мы добавили зависимость модуля app от data.
Следующим шагом будет подключение даггера к проекту и его использование для инджекта Database в TasksActivity.
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
Комментарии
RSS лента комментариев этой записи