- Модули Android приложения
- Создание нового модуля
- 1. Design и Text
- 2. Pallete
- 3. Component Tree
- 4. Режимы представления
- 5. Экран
- 6. Свойства компонентов
- Размещение кнопки
- Обработчик события
- Подключение обработчика события
- Урок 1. Введение
- Зачем нужен Dagger
- Пример
- Теория
- Практика
- Подключение
- Объекты
- Модули
- Компонент
- Get метод
- Inject метод
Модули Android приложения
Структура проекта Android может включать один или несколько различных модулей (Module). В модуле определяется интерфейс экрана и формируется код. Модуль, по сути, является приложением, а проект представляет собой контейнер для модуля/ей. При старте модуля получаем готовое Android-приложение, созданное в этом модуле. В одном проекте может быть несколько модулей, да и проектов также можно создать несколько. При старте какого-либо проекта необходимо указать запускаемый модуль.
При создании первого приложения в структуре проекта можно увидеть запись app – это и есть модуль. По умолчанию при создании нового проекта создается модуль app. Не будем его пока касаться, а будем создавать свои модули. Но удалять его тоже не следует, поскольку мастер (wizard) при создании проекта в основном касался как раз этого модуля.
В данной статье рассмотрим вопрос создания нового модуля (приложения), добавления в интерфейс приложения кнопки и подключение к ней обработчика события нажатия на кнопку.
Создание нового модуля
Создадим новый модуль (приложение), используя первый проект. Процедура создания модуля частично похожа на создание проекта, но с небольшими отличиями. Чтобы создать новый модуль необходимо в меню выбрать File => New => New module.
На первом шаге выбираем интерфейс приложения «Phone and Tablet Application»
На втором шаге определяем параметры модуля :
Application/Library name – это непосредственно наименование приложения, которое будет отображаться в списке приложений в смартфоне. Назовем приложение, к примеру, как p01module.
Module name – это наименование модуля. IDE Android автоматически формирует данное наименование из названия приложения, определяя символы в нижнем регистре. Наименование модуля будет отображаться в структуре проекта, там, где сейчас определен app. Можно ввести свое наименование модуля, я не стал ничего менять.
Package name – имя пакета можно отредактировать вручную, нажав кнопку «edit» справа.
На следующем шаге (скриншот не представлен) выбираем Activity. Что такое Activity?. Это «окно». Приложение Android состоит из окон, называемых Activity. В каждый момент времени обычно отображается одно Activity, которое занимает весь экран. Приложение переключается между окнами. Окна условно можно рассматривать как страницы сайта, между которыми происходит переключение, либо окна ОС Windows. Выбираем Empty Activity и переходим к определению его параметров.
Интерфейс Activity формируется из различных компонентов, называемых View. Самые распространенные View — это кнопка, поле ввода, чекбокс и т.д. Как правило, View помещается в контейнер ViewGroup. Самым распространенным контейнером является Layout, который отвечает за расположение дочерних элементов (таблица, строка, столбец . ).
На этом шаге в компонентах окна мастера определения Activity ничего не меняем и нажимаем кнопку Finish. Через некоторое время Android Studio создаст модуль и разместит его в структуре проекта.
Содержимое модуля (приложения) в структуре проекта включает несколько папок :
- manifests — содержит конфигурационый файл приложения AndroidManifest.xml;
- java — размещает весь написанный код приложения;
- res — используется для файлов-ресурсов различного типа.
В этом модуле интерес представляет файл: res => layout => activity_main.xml . По умолчанию мастер добавил в файл компонент типа TextView, в котором, наряду с остальными параметрами, определил текстовое значение «Hello World!».
Файл activity_main.xml является макетом (шаблоном) приложения или layout-файл. Содержимое activity_main.xml включает набор и расположение View компонентов, которые должны быть отражены на экране. При запуске приложения, Activity читает этот файл и отображает его содержимое в виде компонентов. Если попытаться открыть его двойным щелчком мыши, то Android Studio откроет его в окне (след. скриншот), включающим наборы различных компонентов и панелей.
1. Design и Text
Вкладка Desing связана с графическим отображением экрана, которое представлено на скриншоте сверху. Вкладка Text — это текстовое представление файла activity_main.xml (предыдущий скриншот).
2. Pallete
В разделе палитры располагается список View компонентов, которые можно размещать в интерфейсе : кнопки, поля ввода, чекбоксы, прогресс-бары и прочее.
3. Component Tree
Дерево компонентов, определяющее иерархия View экрана. После создания модуля корневым элементом является ConstraintLayout, включающий TextView.
4. Режимы представления
На скриншоте в области 4 размещен компонент выбора экрана в виде выпадающего списка, позволяющий выбрать один из 3-х режимов представления экрана (5) :
- Design – режим представления экрана с компонентами;
- Blueprint – режим представления экрана в виде контура;
- Design + Blueprint – совмещение двух представлений.
Рядом размещается кнопка (не выделена) определения ориентации экрана : Portrait, Landscape. По умолчанию используется Portrait. Правее располагается кнопка типа смартфона, версия Android и стиль (Thema) представления.
5. Экран
Графический интерфейс экрана приложения. Здесь будут размещаться различные компоненты из области палитры 2.
6. Свойства компонентов
При выделении какого-либо компонента View (3) здесь будут отображаться его свойства. С помощью свойств можно настраивать внешний вид, расположение и содержимое компонента.
Размещение кнопки
Разместим в интерфейсе кнопку под текстовым полем, как это представлено на следующем скриншоте.
В дереве «Component Tree» появился новый компонент (View) «Button».
Обработчик события
Создадим обработчик события btnHello_Click, который по нажатию на кнопку запишет в текстовое поле строку «Здравствуй, мир». По умолчанию в тестовом поле строка «Hello World!». Обработчик события btnHello_Click определим в активности MainActivity.java :
В обработчике события btnHello_Click следует обратить внимание на первую строку, в которой определяется текстовый компонент с использованием метода findViewById. Этому методу необходимо передать идентификатор компонента. В следующей строке в компонент записывается новая текстовая строка.
Подключение обработчика события
Подключение обработчика к событию кнопки onClick можно выполнить либо непосредственно в файле activity_main.xml, либо использовать панель атрибутов. В первом случае необходимо открыть activity_main.xml в текстовом режиме и добавить строку «android:onClick=»btnHello_Click»» в раздел описания кнопки :
Во втором случае необходимо открыть activity_main.xml в режиме Design и в поле кнопки onClick определить наименование обработчика события.
После старта приложения мы увидим наш интерфейс : в верхней части наименование модуля P01module, в центральной части строку «Hello World!», ниже которой располагается кнопка. После нажатия на кнопку текст изменится на «Здравствуй, мир».
Источник
Урок 1. Введение
В этом уроке я подробно расскажу о Dagger и его возможностях. Мы разберем, что такое Component и Module, подключим Dagger к проекту, и сделаем несколько простых примеров
Зачем нужен Dagger
Если вы хотите снизить зависимость объектов друг от друга и упростить написание тестов для вашего кода, то вам подойдет паттерн Dependency Injection. А Dagger — это библиотека, которая поможет в реализации этого паттерна. В этом курсе я опишу использование библиотеки Dagger версии 2 (далее по тексту даггер).
Плюсы даггера в сравнении с другими библиотеками:
— генерирует код несложный для понимания и отладки
— проверяет зависимости на этапе компиляции
— не создает проблем при использовании proguard
Сразу скажу, что тема нетривиальная и у вас могут возникать вопросы типа «а что будет, если сделать так?». Рассмотреть все случаи в рамках этого курса я не смогу. Поэтому очень рекомендую вам создавать примеры и на них проверять, как все это работает в том или ином случае. Это поможет вам лучше понять теорию.
Пример
Чтобы понять, зачем нам может понадобиться Dependency Injection и даггер, давайте рассмотрим небольшой пример. В нем мы смоделируем ситуацию, когда создание одного объекта может потребовать создание еще нескольких.
Пусть в нашем приложении есть MainActivity и, в соответствии с паттерном MVP, для него есть презентер. Презентеру для работы нужны будут некие UserController и DataController. Т.е. нам надо будет создать два этих объекта перед тем, как создать презентер. Но для создания двух этих объектов нам, в свою очередь, нужны объекты ApiService и SharedPreferences. А для создания ApiService нужны RestAdapter, RestAdapter.Builder, OkHttpClient и Cache.
В обычной реализации это может выглядеть так:
В MainActivity мы создаем один объект, затем используем его при создании другого, и так далее по цепочке, чтобы в итоге получить презентер. Нам сейчас не важно, какие именно объекты создаются. Главное — это сколько кода может потребоваться написать в MainActivity, чтобы получить результат.
Выглядит это все не очень красиво, а главное — неправильно. Activity ничего не должно знать о кэшах, сервисах, контроллерах и прочем. Activity знает только презентер и должно получать его в готовом виде.
Как вариант — можно весь этот создающий код поместить в сам презентер. Т.е. Activity только создает презентер, а он уже пусть внутри себя создает все остальные объекты. Так сделать можно, но это тоже неправильно, потому что нарушает принцип Dependency Injection: презентер не должен создавать эти объекты, он их должен получать готовыми. Да и написать нормальные тесты для такого презентера будет затруднительно.
Нам нужен какой-то механизм, который умеет сам создавать все необходимые объекты, как мы это делали в коде Activity. Т.е. по цепочке, создает один объект, использует его в конструкторе другого объекта и так далее, пока не получится готовый презентер, который будет торжественно вручен Activity.
Даггер как раз является таким механизмом. При его использовании код в Activity будет выглядеть так:
Разумеется, код создания объектов никуда не исчез. Но он разделен на части и вынесен из Activity в специальные отдельные классы, к которым даггер имеет доступ. В итоге мы просто вызываем метод getMainActivityPresenter, чтобы получить презентер. А даггер под капотом уже сам создаст этот объект и всю необходимую для него иерархию объектов.
Теория
Теперь давайте смотреть, как работает даггер изнутри.
Возьмем все тот же пример с Activity и Presenter. Т.е. когда Activity для своих нужд создает объект Presenter.
Обычая схема создания будет выглядеть так:
Т.е. Activity создает Presenter самостоятельно
При использовании даггера схема будет выглядеть так:
Activity -> Component -> Module -> Presenter
Activity обращается к компоненту (appComponent в примере выше), компонент с помощью модулей создает Presenter (и все остальные необходимые для этого объекты) и возвращает его в Activity.
Модули и компоненты — это два ключевых понятия даггера.
Модули — это просто классы, в которые мы выносим (из Activity) код создания объектов. Обычно каждый модуль включает в себя создание объектов близких по смыслу.
Модуль UserModule будет содержать в себе код создания объектов, связанных с пользователями, т.е. UserController.
Модуль NetworkModule — объекты OkHttpClient и ApiService.
Модуль StorageModule — объекты DataController и SharedPreferences
Компонент — это посредник между Activity и модулями. Когда Activity нужен какой-либо объект, она сообщает об этом компоненту. Компонент знает, какой модуль умеет создавать такой объект, просит модуль создать объект, и передает его в Activity. При этом компонент может использовать другие модули, чтобы создать всю иерархию объектов, необходимую для создания искомого объекта.
Процесс работы даггера можно сравнить с обедом в McDonalds. Т.е. по аналогии со схемой даггера:
Activity -> Component -> Module -> Presenter
схема McDonalds выглядит так:
Клиент -> Кассир -> Производственная линия -> Заказ (Бигмак/Картошка/Кола)
Рассмотрим подробнее шаги этих схем:
McDonalds | Даггер |
Клиент определился, что его заказ будет состоять из бигмака, картошки и колы, и он говорит об этом кассиру | Activity сообщает компоненту, что ему понадобится Presenter |
Кассир ходит по производственной линии и собирает заказ: берет бигмак, наливает колу, насыпает картошку | Компонент использует модули, чтобы создать все необходимые объекты, которые понадобятся для создания Presenter |
Кассир комплектует заказ в пакет или на поднос и выдает его клиенту | Компонент в итоге получает от модулей требуемый объект Presenter и отдает его Activity |
Практика
Теперь на простом примере посмотрим, как создавать модули и компоненты, и как с их помощью Activity будет получать требуемые объекты.
В этом курсе все примеры будут на Kotlin.
Подключение
Для начала подключите плагин kapt. Это делается в самом начале файла build.gradle модуля
Подключить можно так:
Добавьте в раздел dependencies файла build.gradle модуля строки:
Объекты
В качестве объектов, которые мы будем запрашивать от даггера, используем пару классов: DatabaseHelper и NetworkUtils.
Их реализация нам сейчас не важна, оставляем их пустыми.
Предположим, что эти объекты будут нужны нам в MainActivity.
Презентер пока не используем, чтобы не усложнять пример.
Чтобы получить эти объекты с помощью даггера, нам нужно создать модули и компонент.
Модули
Создаем модули, которые будут уметь предоставлять требуемые объекты. Именно в модулях мы и пишем весь код по созданию объектов. Это обычные классы, но с парой аннотаций.
Модуль для DatabaseHelper:
Модуль для NetworkUtils:
Аннотацией @Module мы сообщаем даггеру, что этот класс является модулем. А аннотация @Provides указывает, что метод является поставщиком объекта.
Таким образом мы предоставляем даггеру информацию о том, как создавать объекты DatabaseHelper и NetworkUtils. Нам больше не надо будет самим создавать их. Теперь, где бы они нам не понадобились, мы сможем просто попросить эти объекты у даггера, и он создаст их и предоставит нам.
Технически можно было вполне обойтись и одним модулем. Но логичнее будет разделить объекты на модули по их смыслу и области применения.
Компонент
Модули готовы, теперь создаем компонент. Для этого нам необходимо создать интерфейс:
Данный интерфейс описывает пустой компонент, который пока ничего не умеет.
При компиляции проекта, даггер найдет этот интерфейс по аннотации @Component и сгенерирует класс DaggerAppComponent (Dagger + имя интерфейса), который станет реализацией этого интерфейса. Этот класс и будет потом создавать объекты и возвращать их нам.
После того как даггер создал нам класс компонента, нам необходимо создать экземпляр этого класса. Это можно сделать, например, в Application классе (не забудьте добавить его в манифест):
У класса компонента есть метод create, которым мы создаем его экземпляр.
Можно, кстати, сделать чуть короче:
На этом месте ваша среда разработки, возможно, будет ругаться на класс DaggerAppComponent. Так может происходить, потому что класса DaggerAppComponent пока не существует. Мы описали интерфейс компонента AppComponent, но нам надо скомпилировать проект, чтобы даггер создал этот класс-компонент.
Скомпилируйте проект. В Android Studio это можно сделать через меню Build -> Make Project (CTRL+F9). После того как процесс завершится, класс DaggerAppComponent будет создан в недрах папки build\generated\. Студия теперь знает этот класс и должна предлагать добавить его в import, чтобы в коде не было никаких ошибок.
Чтобы добраться до компонента из MainActivity, мы можем сделать так:
После этого MainActivity сможет получать от компонента все необходимые объекты.
Нам остается рассказать компоненту, какие именно объекты мы хотим от него получать. Для этого мы будем наполнять его интерфейс методами. И вот тут у нас есть два вида методов.
Первый — это обычный и понятный get метод:
Т.е. мы просто описываем метод, который должен вернуть нужный нам объект. Когда даггер будет создавать класс-реализацию (DaggerAppComponent) этого интерфейса, он создаст в нем и реализацию метода getDatabaseHelper. Этот метод будет ходить в модуль StorageModule, создавать объект DatabaseHelper и возвращать его нам.
Второй вид методов посложнее для понимания. Это inject метод.
В этом случае мы не просим компонент создать и вернуть нам конкретный объект. Вместо этого мы просим компонент проверить какие объекты нужны в MainActivity. Компонент создаст эти объекты и передаст их сразу в MainActivity. Такая процедура называется инджект.
Рассмотрим оба способа подробнее.
Get метод
Нам от компонента нужны объекты DatabaseHelper и NetworkUtils. Для этого нам надо просто добавить в интерфейс компонента методы, которые будут возвращать эти объекты:
Создаем два метода. Они могут быть с любым именем, главное — это их возвращаемые типы (NetworkUtils и DatabaseHelper). Они дают понять компоненту, какие именно объекты мы хотим от него получать. При компиляции, даггер проверит, в каком модуле какой объект можно достать, и нагенерит в реализации двух этих методов соответствующий код создания этих объектов.
Список modules — это модули. Здесь нам надо указать модули, в которых компонент сможет найти код создания объектов. В StorageModule он найдет код для создания DatabaseHelper, а в NetworkModule — для NetworkUtils.
В MainActivity мы просто вызываем эти методы компонента, чтобы получить готовые объекты:
Если этот код у вас крэшит с NullPointerException, убедитесь, что добавили App класс в манифест.
Inject метод
У нас в MainActivity сейчас всего два объекта, которые мы получаем от компонента. Но если их будет штук 20, то придется в интерфейсе компонента описать 20 get-методов и в коде MainActivity написать 20 вызовов этих методов. У даггера есть более удобное решение для таких случаев. Мы можем научить компонент не возвращать объекты, а самому наполнять Activity требуемыми объектами. Т.е. мы даем компоненту экземпляр MainActivity, а он смотрит, какие объекты нужны, создает их и сам помещает в соответствующие поля.
Перепишем интерфейс компонента
Вместо пары get-методов мы описываем один inject-метод. Имя может быть любым, главное — это тип его единственного параметра. Мы указываем здесь MainActivity. Тем самым, мы говорим компоненту, что когда мы будем вызывать этот метод и передавать туда экземпляр MainActivity, мы ожидаем, что компонент наполнит этот экземпляр требуемыми объектами.
Аннотациями @Inject мы помечаем поля, которые компонент должен заполнить (инджектить). При вызове метода injectMainActivity компонент создаст объекты DatabaseHelper и NetworkUtils и поместит их в соответствующие поля MainActivity.
Этот механизм можно посмотреть в коде класса (DaggerAppComponent) компонента, который был сгенерирован даггером. Метод injectMainActivity, если поубирать все лишнее, выглядит примерно так:
Разумеется, get-методы и inject-методы могут быть использованы вместе в одном компоненте. Я описывал их отдельно друг от друга только для простоты понимания.
Где и как размещать все эти классы и интерфейсы зависит от вашей архитектуры и является отдельной темой для дискуссии.
Источник