Учитывая модульность проекта Android

Опубликовано: 2019-08-21

Когда проект достигает определенного масштаба, то дальнейшая работа с ним в рамках одного модуля становится менее эффективной. Модульная структура становится эффективным решением.

4 преимущества модульности

Прежде чем принять решение о модуляризации, хорошо бы точно знать, о чем идет речь. К преимуществам модульной структуры проекта Android относятся:

Лучшая изоляция кода

Каждый модуль может открывать свои общедоступные API-интерфейсы и скрывать детали реализации. С одним модулем нельзя быть полностью уверенным, что его реализация хорошо спрятана (особенно в Kotlin, где недоступен модификатор видимости пакета).

Упрощенная оценка новых технологий

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

Сокращение времени сборки проекта

Изменение модуля требует перестроения этого модуля и других, зависящих от него. Узнайте, как именно это работает, прочитав эту документацию: Android-разработчики. Конфигурации зависимостей

Более удобная работа

Становится легче анализировать/отлаживать/рефакторить небольшие и изолированные фрагменты кода. Кроме того, адаптация новых разработчиков будет проходить быстрее.

Этих преимуществ достаточно, чтобы убедить вас начать процесс модульности, но с чего начать?

# 1: Определите свои модули и их отношения

Существует два подхода к распознаванию ваших модулей: по функциям и по слоям.

Под функциональными модулями вы можете понимать некоторые области приложения, доступные пользователям (например, вход в систему, панель управления, профиль и т. д.). Эти области могут состоять из одного экрана или потока экранов, охватывающих какой-либо процесс. Модули этого типа не могут зависеть от модулей того же типа.

После определения функций вам определенно нужно будет извлечь общие функции , необходимые для нескольких или даже для всех модулей. Эти модули могут отвечать за отдельные уровни архитектуры (такие как постоянное хранилище, сеть, навигация, компоненты пользовательского интерфейса…) или за бизнес-логику обработки некоторых данных, используемых набором функций. Такие модули обычно называют библиотеками . Модули библиотеки могут создавать деревья зависимостей.

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

Модули и их отношения в приложениях

С учетом приведенных выше определений иерархия модулей может выглядеть следующим образом:

# 2: Настройка внедрения зависимостей

Несмотря на зависимости между модулями проекта, вы также должны настроить кинжальные зависимости. Dagger предлагает два способа объявления зависимости: подкомпоненты и зависимость компонентов .

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

Зависимость компонента Dagger более гибкая, потому что дочерний компонент может объявить, что он зависит от родителя. Это позволяет использовать такую ​​зависимость между отдельными модулями проекта.

В какой-то момент вы можете обнаружить, что один модуль нуждается в ограниченных знаниях о другом модуле. Очень хорошим примером этого может быть навигация между функциональными модулями. Предоставление такого рода отношений часто называют горизонтальной зависимостью . Для создания этого канала связи между отдельными модулями необходимы дополнительные модули с интерфейсами, которые описывают эту связь, и модуль, который будет привязывать реализацию к объявленным интерфейсам.

Настройка зависимости модуля проекта для управления горизонтальной зависимостью представлена ​​на изображении ниже:

Настройка зависимости модуля проекта

Пример кода для таких отношений приведен в проекте в конце статьи.

# 3: Настройка Gradle

Каждый модуль проекта имеет свой gradle.build, который почти такой же, за исключением добавления зависимостей и применения плагинов. Так что приятно извлекать повторяющуюся конфигурацию в один файл gradle в корне каталога проекта. Такой файл также может регистрировать общие задачи Gradle для выполнения статического анализа кода или запуска модульного тестирования.

Фрагмент кода такой общей настройки можно найти здесь:

 afterEvaluate { project -> def isAndroid = project.plugins.hasPlugin('com.android.library') || project.plugins.hasPlugin('com.android.application') setupModule(isAndroid) setupCommonTestDependencies(isAndroid) setupCommonTasks(isAndroid) } def setupModule(isAndroid) { if (isAndroid) { android { compileSdkVersion projectCompileSdk defaultConfig { minSdkVersion projectMinSdk targetSdkVersion projectTargetSdk } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { abortOnError true checkReleaseBuilds false checkAllWarnings true warningsAsErrors true def lintBaseline = file("quality/lint-baseline.xml") if (lintBaseline.exists()) baseline lintBaseline } } } else { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } } def setupCommonTestDependencies(isAndroid) { dependencies { testImplementation "junit:junit:${junitVersion}" testImplementation "org.assertj:assertj-core:${assertJVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}" testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:${mockitoKotlinVersion}" if (isAndroid) { androidTestImplementation "androidx.test.ext:junit:${axTestJUnitVersion}" androidTestImplementation "androidx.test.espresso:espresso-core:${axEspressoLibVersion}" } } } def setupCommonTasks(isAndroid) { if (isAndroid) { tasks.register("unitTest") { task -> task.dependsOn(testDebugUnitTest) } } else { tasks.register("unitTest") { task -> task.dependsOn(test) } } }

Вывод

Эта статья не является исчерпывающим или полным руководством по модуляризации проекта Android. Но я думаю, что он затрагивает аспекты, которые вы должны учитывать при начале модульности проекта.

Фрагмент кода для демонстрации горизонтальной зависимости можно найти по ссылке.

Хотите создать нативное приложение для Android? Выбирайте Микидо!