DEV Community

Rodrigo Sicarelli
Rodrigo Sicarelli

Posted on • Edited on

Android Plataforma - Parte 9: Unificando a Application e Library extensions com a Common Extension

🌱 Branch: 9/android-commons-extension

🔗 Repositório: github.com/rsicarelli/kotlin-gradle-android-platform

⬅️ Artigo Anterior: Parte 8: Decorando os módulo 'library'

➡️ Próximo Artigo: Parte 10: Customização dos módulos


No último post, conseguimos extrair a lógica de configuração de nossos módulos library/biblioteca.

Mas, notamos que houve muita duplicação de código, além de não ter uma "fonte da verdade" para partes cruciais, por exemplo configurações de sdk.

Nesse post, iremos entender como unificar essas duas partes em comum, falar sobre a ApplicationExtension, LibraryExtension e CommonExtension e ter uma função que aplique as configurações comuns para qualquer tipo de módulo Android.


Extensions do Android Gradle Plugin (AGP)

No universo do desenvolvimento Android, o Google nos presenteia com uma DSL consistente e poderosa dentro dos nossos arquivos build.gradle.kts.

No entanto, quando começamos a utilizar essas extensões em nossa plataforma, percebemos que há várias features "pequenas" que, embora sutis, têm o poder de redefinir como configuramos nossos módulos.

ApplicationExtension

A porta de entrada para desenvolvimendo Android, é a extensão android { } no build.gradle.kts.

Esta extensão é, na verdade, uma representação da ApplicationExtension quando o plugin com.android.application está aplicado.

A ApplicationExtension é uma extensão específica do Android Gradle Plugin para a configuração de diversos aspectos de um projeto Android.

Adicionalmente, ela herda características de outras extensões, como CommonExtension, ApkExtension e TestedExtension, ampliando suas capacidades.

interface ApplicationExtension :
    CommonExtension<
            ApplicationBuildFeatures,
            ApplicationBuildType,
            ApplicationDefaultConfig,
            ApplicationProductFlavor,
            ApplicationAndroidResources>,
    ApkExtension,
    TestedExtension { }
Enter fullscreen mode Exit fullscreen mode

LibraryExtension

Na criação de bibliotecas Android, a extensão android assume uma forma distinta.

Ela é uma representação da LibraryExtension quando o plugin com.android.library está aplicado.

A LibraryExtension é específica para o plugin de biblioteca Android e fornece meios para configurar e personalizar uma biblioteca Android, diferenciando-se dos projetos de aplicativos.

Além disso, ela tmbém herda características da CommonExtension e TestedExtension, disponibilizando várias configurações e opções comuns a todos os tipos de projetos Android.

interface LibraryExtension :
    CommonExtension<
        LibraryBuildFeatures,
        LibraryBuildType,
        LibraryDefaultConfig,
        LibraryProductFlavor,
        LibraryAndroidResources>,
    TestedExtension { }
Enter fullscreen mode Exit fullscreen mode

CommonExtension

Esta extensão serve como base para configurações compartilhadas entre diferentes tipos de projetos Android, como aplicativos, bibliotecas e testes instrumentados.

CommonExtension não é apenas uma extensão. É uma interface genérica que define um conjunto de propriedades e métodos comuns a todos os projetos Android. Isso significa que ela estabelece um contrato de propriedades e métodos que devem estar disponíveis para qualquer extensão que dela herde.

interface CommonExtension<
        BuildFeaturesT : BuildFeatures,
        BuildTypeT : BuildType,
        DefaultConfigT : DefaultConfig,
        ProductFlavorT : ProductFlavor,
        AndroidResourcesT : AndroidResources> {}
Enter fullscreen mode Exit fullscreen mode

Por que é tão poderosa?

Em vez de definir configurações específicas para cada tipo de projeto (aplicativo, biblioteca, etc.) separadamente, pode-se confiar que certas configurações serão consistentes e compartilhadas entre os módulos.

Isso não só reduz a complexidade mas também diminui a probabilidade de erros de configuração. Por exemplo, ao definir uma configuração de compilação comum para todos os módulos Android, fazendo isso através da CommonExtension garante-se que essa configuração seja aplicada de forma uniforme a todos.

Quando extensões específicas, como ApplicationExtension ou LibraryExtension, são criadas, elas implementam a CommonExtension, herdando todas as suas características.

Assim, qualquer propriedade ou método definido em CommonExtension estará automaticamente disponível para qualquer outra extensão que dela herde. Isso assegura uma estrutura de base comum e consistente em todos os projetos Android.

Image description

Extraíndo decorações comuns utilizando a CommonExtensions

1 - Dentro do arquivo build-logic/decorations/android.kt, crie o seguinte atributo:

import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.findByType

private val Project.commonExtension: CommonExtension<*, *, *, *, *>
    get() = extensions.findByType<ApplicationExtension>()
        ?: extensions.findByType<LibraryExtension>()
        ?: error("Android plugin not applied")
Enter fullscreen mode Exit fullscreen mode

Essa função irá se encarregar de se utilizar de uma das extensões ApplicationExtension ou LibraryExtension e utilizar da herança para retornar o tipo CommonExtension<*, *, *, *, *>

2 - Vamos criar uma função private fun applyAndroidCommon() e utilizar essa nosso novo atributo:

private fun Project.applyAndroidCommon() =
    with(commonExtension) {
      // this é CommonExtension<*, *, *, *, *>   
    }
Enter fullscreen mode Exit fullscreen mode

3 - Vamos trazer tudo que é compartilhado para dentro dessa função. Resultado final fica assim:

private fun Project.applyAndroidCommon() =
    with(commonExtension) {
        namespace = "com.rsicarelli.kplatform"
        compileSdk = 34

        defaultConfig {
            minSdk = 24

            vectorDrawables {
                useSupportLibrary = true
            }
        }

        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_17
            targetCompatibility = JavaVersion.VERSION_17
        }

        applyKotlinOptions()

        buildFeatures {
            compose = true
        }

        composeOptions {
            kotlinCompilerExtensionVersion = libs.version("composeKotlinCompilerExtension")
        }

        packaging {
            resources {
                excludes += "/META-INF/{AL2.0,LGPL2.1}"
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

4 - Vamos atualizar nossa applyAndroidApp(), mantendo as configurações específicas do ApplicationExtension, como applicationId, etc:

internal fun Project.applyAndroidApp() {
    applyAndroidCommon()

    extensions.configure<ApplicationExtension> {
        defaultConfig {
            applicationId = "com.rsicarelli.kplatform"
            targetSdk = 34
            versionCode = 1
            versionName = "1.0"
        }

        buildTypes {
            release {
                isMinifyEnabled = false
                proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

5 - Vamos fazer o mesmo com a applyAndroidLibrary():

internal fun Project.applyAndroidLibrary() {
    applyAndroidCommon()

    extensions.configure<LibraryExtension> {
        buildTypes {
            release {
                isMinifyEnabled = false
                proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Note que a função buildTypes está disponível na CommonExtensions, mas a função release não está. Por hora, vamos duplicar essa parte, que será abordada no próximo artigo

Sucesso!

Conseguimos dar um grande passo compartilhando os comportamentos utilizando CommonExtension.

Top comments (0)