Considerando a modularização do projeto Android

Publicados: 2019-08-21

Quando um projeto atinge uma certa escala, o trabalho adicional com ele em um único módulo se torna menos eficaz. A modularização torna-se então uma solução eficaz.

4 vantagens da modularização

Antes de decidir sobre a modularização, é bom esclarecer exatamente o que está envolvido. As vantagens de uma estrutura de projeto modular do Android incluem:

Melhor isolamento de código

Cada módulo pode expor suas interfaces de API públicas e ocultar detalhes de implementação. Com um único módulo, você não pode ter certeza absoluta de que sua implementação está bem oculta (especialmente em Kotlin, onde um modificador de visibilidade de pacote não está disponível).

Avaliação mais fácil de novas tecnologias

Ao criar um módulo, você pode verificar o novo padrão de arquitetura ou a nova biblioteca sem afetar os outros módulos.

Redução do tempo de construção do projeto

A modificação de um módulo requer a reconstrução desse módulo e de outros que dependem dele. Veja exatamente como funciona lendo esta documentação: Desenvolvedores Android. Configurações de dependência

Trabalho mais conveniente

Torna-se mais fácil analisar/depurar/refatorar pedaços de código menores e isolados. Além disso, a integração de novos desenvolvedores será mais rápida.

Esses benefícios parecem suficientes para convencê-lo a iniciar o processo de modularização, mas como você começa?

#1: Identifique seus módulos e suas relações

Existem duas abordagens para reconhecer seus módulos: por recurso e por camada.

Nos módulos de recursos, você pode entender alguma área do aplicativo disponível para os usuários (por exemplo, login, painel, perfil etc.). Essas áreas podem consistir em uma única tela ou um fluxo de telas cobrindo algum processo. Módulos deste tipo não podem depender de módulos do mesmo tipo.

Depois de identificar os recursos, você definitivamente precisará extrair funcionalidades comuns exigidas por alguns ou até mesmo por todos os módulos. Esses módulos podem ser responsáveis ​​por camadas de arquitetura separadas (como armazenamento persistente, rede, navegação, componentes de interface do usuário…) ou pela lógica de negócios de processamento de alguns dados usados ​​por um conjunto de recursos. Esses tipos de módulos são geralmente chamados de bibliotecas . Módulos de biblioteca podem construir árvores de dependência.

Além dos módulos de recursos e bibliotecas, há também a necessidade de um módulo para gerenciar conexões horizontais entre outros módulos (mais sobre isso no próximo ponto). Este módulo conterá uma classe de aplicativo personalizada e uma configuração de injeção de dependência. Nenhum outro módulo pode depender deste módulo, mas este módulo depende de todos os outros no projeto.

Módulos e suas relações em aplicativos

Levando em conta as definições acima, a hierarquia dos módulos pode ficar assim:

#2: Configuração de injeção de dependência

Apesar das dependências entre os módulos do projeto, você também deve configurar as dependências da adaga. O Dagger oferece duas maneiras de declarar dependência: subcomponentes e dependência de componente .

A dependência do subcomponente Dagger exige que os pais declarem todos os filhos dependentes. Entre os módulos do projeto, esse tipo de relação não funcionaria, pois inverte a direção da dependência do módulo do projeto. Mas pode ser usado em módulos de projeto separados.

A dependência do componente Dagger é mais flexível porque um filho pode declarar que é dependente do pai. Isso torna possível usar esse tipo de dependência entre módulos de projeto separados.

Em algum momento, você pode descobrir que um módulo precisa de conhecimento limitado sobre outro módulo. Um bom exemplo disso pode ser a navegação entre os módulos de recursos. Fornecer esse tipo de relação é freqüentemente chamado de dependência horizontal . Para criar este canal de comunicação entre módulos separados são necessários módulos adicionais com interfaces que descrevam esta comunicação e módulo que irá vincular uma implementação às interfaces declaradas.

A configuração da dependência do módulo do projeto para gerenciar a dependência horizontal é apresentada na imagem abaixo:

Configuração de dependência do módulo do projeto

O código de exemplo para tais relações é fornecido em um projeto no final do artigo.

#3: Configuração do Gradle

Cada módulo de projeto tem seu gradle.build, que é praticamente o mesmo, exceto que dependências e plugins adicionados são aplicados. Portanto, é bom extrair configurações repetitivas para um arquivo gradle na raiz do diretório do projeto. Esse arquivo também pode registrar tarefas comuns do gradle para executar análise estática de código ou executar testes de unidade.

O trecho de código dessa configuração comum é encontrado aqui:

 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) } } }

Conclusão

Este artigo não é exaustivo ou um guia completo para modularizar um projeto Android. Mas acho que aborda aspectos que você deve considerar ao iniciar a modularização do projeto.

Um pedaço de código para mostrar uma dependência horizontal é encontrado no link.

Quer construir um aplicativo nativo para Android? Escolha Miquido!