DEV Community

Rodrigo Sicarelli
Rodrigo Sicarelli

Posted on • Edited on

Android Plataforma - Parte 11: Criando uma DSL para customizar as novas opções

🌱 Branch: 10-11/customizing-android-options

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

⬅️ Artigo Anterior: Parte 10: Customização dos módulos

➡️ Próximo Artigo: Parte 12: Otimizando tempo de compilação para bibliotecas Android


No artigo anterior, parametrizamos os argumentos de applyAndroidApp() e appyAndroidLibary() com modelos.

Agora, as funções androidApp() e androidLibrary() devem ser modificadas para aplicar as devidas decorações nos módulos.


Definindo os valores através de uma DSL

Dentro das nossas funções androidApp() e androidLibrary() poderiamos simplesmente aceitar um modelo:

fun Project.androidApp(androidAppOptions: AndroidAppOptions) = applyAndroidApp(androidAppOptions)

fun Project.androidLibrary(androidLibraryOptions: AndroidLibraryOptions) = applyAndroidLibrary(androidLibraryOptions)
Enter fullscreen mode Exit fullscreen mode

Essa é uma abordagem totalmente válida! Porém, na hora de consumir, precisamos ter um "boilerplate" de definir uma nova classe:

    androidApp(
        androidAppOptions = AndroidAppOptions(
            applicationId = "com.rsicarelli.plataforma",
            ..
        )
    )
Enter fullscreen mode Exit fullscreen mode

Isso é bem verboso, além de fugir um pouco do estilo "convencional" de DSL que encontramos nos arquivos build.gradle.kts.

Para solucionar esse problema, vamos introduzir uma DSL que cuide dessa customização de uma forma elegante e idiomática no Kotlin.

Note que, aqui iremos definir os valores padrões da nossa plataforma.

Image description


abstract class AndroidOptionsBuilder {

    var namespace: String = "com.rsicarelli.kplatform"
    var compileSdk: Int = 34
    var minSdk: Int = 24
    var useVectorDrawables: Boolean = true
    var javaVersion: JavaVersion = JavaVersion.VERSION_17
    var composeOptions: ComposeOptions = ComposeOptions()
    var packagingOptions: PackagingOptions = PackagingOptions()
    var buildTypes: List<AndroidBuildType> = listOf(ReleaseBuildType, DebugBuildType)

    abstract fun build(): AndroidOptions
}

class AndroidAppOptionsBuilder : AndroidOptionsBuilder() {

    var applicationId: String = "com.rsicarelli.kplatform"
    var targetSdk: Int = 34
    var versionCode: Int = 1
    var versionName: String = "1.0"
    private var proguardOptionsBuilder = ProguardOptionsBuilder("proguard-rules.pro")

    fun proguardOptions(init: ProguardOptionsBuilder.() -> Unit) {
        proguardOptionsBuilder.apply(init)
    }

    override fun build(): AndroidAppOptions = AndroidAppOptions(
        applicationId = applicationId,
        targetSdk = targetSdk,
        versionCode = versionCode,
        versionName = versionName,
        proguardOptions = proguardOptionsBuilder.build(),
        namespace = namespace,
        compileSdk = compileSdk,
        minSdk = minSdk,
        useVectorDrawables = useVectorDrawables,
        javaVersion = javaVersion,
        composeOptions = composeOptions,
        packagingOptions = packagingOptions,
        buildTypes = buildTypes
    )
}

class AndroidLibraryOptionsBuilder : AndroidOptionsBuilder() {

    private var proguardOptionsBuilder = ProguardOptionsBuilder("consumer-proguard-rules.pro")

    fun proguardOptions(init: ProguardOptionsBuilder.() -> Unit) {
        proguardOptionsBuilder.apply(init)
    }

    override fun build(): AndroidLibraryOptions = AndroidLibraryOptions(
        proguardOptions = proguardOptionsBuilder.build(),
        namespace = namespace,
        compileSdk = compileSdk,
        minSdk = minSdk,
        useVectorDrawables = useVectorDrawables,
        javaVersion = javaVersion,
        composeOptions = composeOptions,
        packagingOptions = packagingOptions,
        buildTypes = buildTypes
    )
}

class ProguardOptionsBuilder(defaultFileName: String) {

    var fileName: String = defaultFileName
    var applyWithOptimizedVersion: Boolean = true

    fun build(): ProguardOptions = ProguardOptions(
        fileName = fileName,
        applyWithOptimizedVersion = applyWithOptimizedVersion
    )
}
Enter fullscreen mode Exit fullscreen mode

Expondo nossos builder nas funções androidApp() e androidLibrary()

Note que passamos uma lambda vazia como parametro, possibilitando o módulo simplesmente invocar com as opções pre-definidas.

fun Project.androidApp(builderAction: AndroidAppOptionsBuilder.() -> Unit = { }) =
    applyAndroidApp(AndroidAppOptionsBuilder().apply(builderAction).build())

fun Project.androidLibrary(builderAction: AndroidLibraryOptionsBuilder.() -> Unit = { }) =
    applyAndroidLibrary(AndroidLibraryOptionsBuilder().apply(builderAction).build())
Enter fullscreen mode Exit fullscreen mode

Uso

Uso é super flúido, olha só como podemos customizar versionCode e versionName no app/build.gradle.kts:

androidApp {
    // this é o AndroidAppOptionsBuilder
    versionCode = 1
    versionName = "1.0.0"

    proguardOptions {
        // this é ProguardOptionsBuilder
        applyWithOptimizedVersion = true
    }
}
Enter fullscreen mode Exit fullscreen mode

Sucesso!

Agora, nossa configuração está elegante com uma DSL expressiva e intuitiva, permitindo diversas customizações adaptáveis para diferentes cenários.

Essa abordagem nos permite estabelecer comportamentos padrão para os módulos, mas também oferece uma DSL robusta para que o time consiga adicionar novas configurações conforme necessário.

No próximo artigo, vamos adicionar uma decoração importantíssima para otimizar o tempo de compilação dos nossos módulos.

Top comments (0)