Considerando la modularización del proyecto Android
Publicado: 2019-08-21Cuando un proyecto alcanza cierta escala, el trabajo posterior con él en un solo módulo se vuelve menos efectivo. Modularizarlo se convierte entonces en una solución eficaz.
4 ventajas de la modularización
Antes de decidirse por la modularización, es bueno tener claro en qué consiste exactamente. Las ventajas de una estructura de proyecto modular de Android incluyen:
Mejor aislamiento de código
Cada módulo puede exponer sus interfaces API públicas y ocultar detalles de implementación. Con un solo módulo, no puede estar completamente seguro de que su implementación esté bien oculta (especialmente en Kotlin, donde un modificador de visibilidad del paquete no está disponible).
Evaluación de nuevas tecnologías más fácil
Cuando crea un módulo, puede verificar el nuevo patrón de arquitectura o la nueva biblioteca sin afectar a los otros módulos.
Reducción del tiempo de construcción del proyecto
Modificar un módulo requiere reconstruir ese módulo y otros que dependen de él. Vea exactamente cómo funciona leyendo esta documentación: desarrolladores de Android. Configuraciones de dependencia
Trabajo más conveniente
Se vuelve más fácil analizar/depurar/refactorizar piezas de código más pequeñas y aisladas. Además, la incorporación de nuevos desarrolladores será más rápida.
Estos beneficios parecen suficientes para convencerlo de comenzar el proceso de modularización, pero ¿cómo comienza?
#1: Identifica tus módulos y sus relaciones
Hay dos enfoques para reconocer sus módulos: por característica y por capa.
En los módulos de funciones, puede comprender algunas áreas de la aplicación disponibles para los usuarios (p. ej., inicio de sesión, panel, perfil, etc.). Estas áreas pueden consistir en una sola pantalla o un flujo de pantallas que cubren algún proceso. Los módulos de este tipo no pueden depender de módulos del mismo tipo.
Después de identificar las funciones, definitivamente necesitará extraer las funcionalidades comunes requeridas por algunos o incluso todos los módulos. Esos módulos pueden ser responsables de capas de arquitectura separadas (como almacenamiento persistente, redes, navegación, componentes de interfaz de usuario...) o la lógica comercial de procesar algunos datos utilizados por un conjunto de funciones. Ese tipo de módulos se suelen llamar bibliotecas . Los módulos de biblioteca pueden construir árboles de dependencia.
Además de los módulos de funciones y bibliotecas, también existe la necesidad de un módulo para administrar las conexiones horizontales entre otros módulos (más sobre esto en el siguiente punto). Este módulo contendrá una clase de aplicación personalizada y una configuración de inyección de dependencia. Ningún otro módulo puede depender de este módulo, pero este módulo depende de todos los demás en el proyecto.

Teniendo en cuenta las definiciones anteriores, la jerarquía de módulos puede verse así:
#2: configuración de inyección de dependencia
A pesar de las dependencias entre los módulos del proyecto, también debe configurar las dependencias dagger. Dagger ofrece dos formas de declarar dependencia: subcomponentes y dependencia de componentes.
La dependencia del subcomponente Dagger requiere que los padres declaren todos los hijos dependientes. Entre los módulos del proyecto, este tipo de relación no funcionaría porque invierte la dirección de dependencia del módulo del proyecto. Pero se puede usar dentro de módulos de proyecto separados.

La dependencia del componente Dagger es más flexible porque un hijo puede declarar que depende del padre. Esto hace posible usar este tipo de dependencia entre módulos de proyecto separados.
En algún momento, puede encontrar que un módulo necesita un conocimiento limitado sobre otro módulo. Un muy buen ejemplo de esto puede ser la navegación entre módulos de funciones. Proporcionar este tipo de relación a menudo se denomina dependencia horizontal . Para crear este canal de comunicación entre módulos separados, se necesitan módulos adicionales con interfaces que describan esta comunicación y un módulo que vinculará una implementación a las interfaces declaradas.
La configuración de la dependencia del módulo del proyecto para administrar la dependencia horizontal se presenta en la siguiente imagen:

El código de ejemplo para tales relaciones se proporciona en un proyecto al final del artículo.
#3: configuración de Gradle
Cada módulo de proyecto tiene su gradle.build, que es más o menos lo mismo, excepto que se aplican dependencias y complementos agregados. Por lo tanto, es bueno extraer la configuración repetitiva a un archivo gradle en la raíz del directorio del proyecto. Dicho archivo también puede registrar tareas comunes de Gradle para ejecutar análisis de código estático o ejecutar pruebas unitarias.
El fragmento de código de dicha configuración común se encuentra aquí:
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) } } }
Conclusión
Este artículo no es exhaustivo ni una guía completa para modularizar un proyecto de Android. Pero creo que aborda aspectos que debes tener en cuenta al iniciar la modularización de un proyecto.
En el enlace se encuentra un fragmento de código para mostrar una dependencia horizontal.
¿Quieres construir una aplicación nativa para Android? ¡Elige Miquido!