DEV Community

Rodrigo Sicarelli
Rodrigo Sicarelli

Posted on • Edited on

Android Plataforma - Parte 13: Incluindo módulos "puro JVM"

🌱 Branch: 13/jvm-only-modules

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

⬅️ Artigo Anterior: Parte 12: Otimizando tempo de compilação para bibliotecas Android

➡️ Próximo Artigo: Parte 14: Aderindo a funcionalidades experimentais do compilador do Kotlin


No último artigo, otimizamos a compilação dos módulos Android desativando diversas funcionalidades do Android Gradle Plugin (AGP).

Neste artigo, discutiremos a distinção entre módulos puro JVM (java-library) e módulos Library Android (com.android.library), além de expandir nossa plataforma para suportar essa funcionalidade.


O que são Módulos Puro JVM?

Módulos Puro JVM são aqueles que utilizam exclusivamente a JVM (Java Virtual Machine) para sua execução. Em outras palavras, não têm vínculo nem dependência direta com o Android.

Simplificando, são módulos puramente Java, isentos das especificidades e complexidades dos módulos Android.

Como resultado, as compilações são mais eficientes, já que esses módulos não passam pelas etapas de compilação do Android Gradle Plugin (AGP).

Quando usar módulos puro JVM?

  1. Lógica de negócios: Para códigos relacionados à lógica de negócios que não dependem diretamente do Android, como cálculos, validações ou manipulações de listas.

  2. Bibliotecas Genéricas: Se você está desenvolvendo uma biblioteca que pode ser usada tanto em projetos Android quanto em projetos Kotlin/JVM puros.

  3. Módulos 'core': Módulos relacionados a banco de dados, rede, logging, que podem ser construídos puramente em Kotlin/JVM.

Decorando nossa plataforma para receber módulos puro JVM

A proposta é adicionar um novo ponto de entrada chamado jvmLibrary() e decorá-lo com a função applyJvmLibrary().

1 - Crie uma função fun Project.applyJvmLibrary() no arquivo kotlin.kt:

import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

internal fun Project.applyJvmLibrary() {
    pluginManager.apply("java-library")
    extensions.configure<JavaPluginExtension> {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    applyKotlinOptions()
}

internal fun Project.applyKotlinOptions() {
    tasks.withType<KotlinCompile>().configureEach {
        kotlinOptions {
            jvmTarget = "17"
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

2 - Vamos manter o padrão da nossa plataforma e possibilitar que os módulos configurem a compilação individualmente. Isso irá possibilitar algumas novas customizações que iremos aplicar no próximo post:

Pra não deixar os Options jogados por aí, vamos criar uma pasta build-logic/src/../options e trazer AndroidOptions.kt para lá.

Crie uma classe CompilationsOptions e declare o conteúdo:

import org.gradle.api.JavaVersion

internal data class CompilationOptions(
    val javaVersion: JavaVersion,
    val jvmTarget: String,
    val allWarningsAsErrors: Boolean,
)

class CompilationOptionsBuilder {

    var javaVersion: JavaVersion = JavaVersion.VERSION_17
    var jvmTarget: String = "17"
    var allWarningsAsErrors: Boolean = false

    internal fun build(): CompilationOptions = CompilationOptions(
        javaVersion = javaVersion,
        jvmTarget = jvmTarget,
        allWarningsAsErrors = allWarningsAsErrors
    )
}
Enter fullscreen mode Exit fullscreen mode

3 - Vamos adaptar nossa função applyJvmLibrary() pra receber um CompilationOptions:

import com.rsicarelli.kplatform.options.CompilationOptions
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

internal fun Project.applyJvmLibrary(compilationOptions: CompilationOptions) {
    pluginManager.apply("java-library")
    applyJavaCompatibility(compilationOptions.javaVersion)
    applyKotlinOptions(compilationOptions)
}

internal fun Project.applyKotlinOptions(compilationOptions: CompilationOptions) {
    tasks.withType<KotlinCompile>().configureEach {
        kotlinOptions {
            allWarningsAsErrors = compilationOptions.allWarningsAsErrors
            jvmTarget = compilationOptions.jvmTarget
        }
    }
}

private fun Project.applyJavaCompatibility(javaVersion: JavaVersion) {
    extensions.configure<JavaPluginExtension> {
        sourceCompatibility = javaVersion
        targetCompatibility = javaVersion
    }
}
Enter fullscreen mode Exit fullscreen mode

4 - Note que alteramos a assinatura da função applyKotlinOptions que está sendo compartilhada entre nossas decorações android.kt.

internal fun Project.applyAndroidApp(
    androidAppOptions: AndroidAppOptions,
    compilationOptions: CompilationOptions,
) {
    applyAndroidCommon(
        androidOptions = androidAppOptions,
        compilationOptions = compilationOptions
    )
    ..
}

internal fun Project.applyAndroidLibrary(
    androidLibraryOptions: AndroidLibraryOptions,
    compilationOptions: CompilationOptions,
) {
    applyAndroidCommon(
        androidOptions = androidLibraryOptions,
        compilationOptions = compilationOptions
    )
        ..
}

private fun Project.applyAndroidCommon(
    androidOptions: AndroidOptions,
    compilationOptions: CompilationOptions,
) =
    with(commonExtension) {
        ..

        compileOptions {
            sourceCompatibility = androidOptions.javaVersion
            targetCompatibility = androidOptions.javaVersion
        }

        applyKotlinOptions(compilationOptions)
        ..
    }
Enter fullscreen mode Exit fullscreen mode

Expondo as novas APIs

Agora, atualizaremos nosso KPlatformPlugin.kt com as novas definições:

fun Project.androidApp(
    compilationOptionsBuilder: CompilationOptionsBuilder.() -> Unit = { },
    appOptionsBuilder: AndroidAppOptionsBuilder.() -> Unit = { },
) = applyAndroidApp(
    androidAppOptions = AndroidAppOptionsBuilder().apply(appOptionsBuilder).build(),
    compilationOptions = CompilationOptionsBuilder().apply(compilationOptionsBuilder).build()
)

fun Project.androidLibrary(
    compilationOptionsBuilder: CompilationOptionsBuilder.() -> Unit = { },
    libraryOptionsBuilder: AndroidLibraryOptionsBuilder.() -> Unit = { },
) = applyAndroidLibrary(
    androidLibraryOptions = AndroidLibraryOptionsBuilder().apply(libraryOptionsBuilder).build(),
    compilationOptions = CompilationOptionsBuilder().apply(compilationOptionsBuilder).build()
)

fun Project.jvmLibrary(builderAction: CompilationOptionsBuilder.() -> Unit = { }) =
    applyJvmLibrary(
        compilationOptions = CompilationOptionsBuilder().apply(builderAction).build()
    )
Enter fullscreen mode Exit fullscreen mode

Criando um módulo JVM e aplicando as decorações da plataforma

1 - Dentro de core, criaremos um novo módulo chamado threading.

Image description

2 - Inclua esse novo módulo no settings.gradle.kts:

include(":app", ":features:details", ":features:home", ":core:designsystem", ":core:threading")
Enter fullscreen mode Exit fullscreen mode

3 - Sincronize o projeto. Em seguida, crie um arquivo build.gradle.kts e configure as opções desse módulo:

import com.rsicarelli.kplatform.jvmLibrary

plugins {
    kotlin("jvm")
}

jvmLibrary()

dependencies {
    api(libs.kotlinx.coroutines.core)
    testApi(libs.kotlinx.coroutines.test)
}
Enter fullscreen mode Exit fullscreen mode

Sucesso!

A IDE informará se essa biblioteca é pura JVM.

Embora eu esteja usando o IntelliJ, o Android Studio também exibirá um ícone diferente
Image description

No próximo artigo, aprenderemos a customizar nossas compilações para incorporar algumas das funcionalidades experimentais do compilador Kotlin.

Image description

Image description

Top comments (0)