🌱 Branch: 12/improving-android-library-build-time
🔗 Repositório: github.com/rsicarelli/kotlin-gradle-android-platform
⬅️ Artigo Anterior: Parte 11: Criando uma DSL para customizar as novas opções
➡️ Próximo Artigo: Parte 13: Incluindo módulos "puro JVM"
--
No último post, construímos uma DSL robusta que nos permite ter um ajuste fino das nossas decorações.
Agora, vamos entender como podemos reduzir o tempo de compilação das nossas nossas android library
configurando as "build features"
"Build features" do Android Gradle Plugin (AGP)
A classe BuildFeatures
é uma interface que define uma lista de características de "build" que podem ser habilitadas ou desabilitadas em um projeto Android.
É por meio dessas funcionalidades que classes como R
e BuildConfig
são geradas, ou que as pastas res
e resource
são reconhecidas e incluídas no projeto.
Funções e suas responsabilidades
aidl: AIDL (Android Interface Definition Language) permite definir as interfaces de comunicação que os componentes de um IPC (Inter-Process Communication) usarão. Em outras palavras, é uma forma de determinar como diferentes processos (ou apps) irão se comunicar.
buildConfig: Esta funcionalidade gera automaticamente uma classe
BuildConfig
com informações meta sobre o aplicativo, como sua versão e se édebug
.compose: Compose é uma toolkit de UI moderna nativa do Android para criação de interfaces.
prefab: Prefab permite a importação de bibliotecas C e C++ como módulos em projetos Android. Essas bibliotecas são empacotadas como AARs (Android ARchive) e facilitam a integração de código nativo nos projetos Android.
renderScript: RenderScript é uma API do Android para execução de computações de alto desempenho, útil para apps que processam gráficos ou realizam cálculos intensivos.
resValues: Facilita a geração de Valores de Recurso, que são constantes colocadas em um arquivo XML para uso no aplicativo.
shaders: Shaders são programas especializados usados para renderizar gráficos, frequentemente empregados em jogos e outras aplicações gráficas intensivas.
viewBinding: O View Binding gera automaticamente um código de vinculação de dados para suas views, melhorando a eficiência e segurança da programação da UI no Android.
Comportamento padrão
Por padrão, tanto o módulo app
quanto library
terão estas funcionalidades habilitadas:
- aidl
- buildConfig
- renderScript
- resValues
- shaders
Funcionalidades desabilitadas por padrão (valor false
):
- compose
- prefab
- viewBinding
Delegando o controle de build features para nossa Plataforma
Sabendo que essas funcionalidades adicionam tempo de compilação, podemos torná-las false
por padrão e delegar essa configuração para nossa Plataforma.
Definindo BuildFeatures
e BuildFeaturesBuilder
1 - Crie um novo modelo e builder para expressar as opções customizadas.
Note que apenas o AndroidLibraryOptions
irá receber essas opções. O motivo é que um app
precisa de todos esses recursos habilitados, então nem damos trabalho de expor essa api no AndroidAppOptions
também.
data class AndroidLibraryOptions(
val buildFeatures: BuildFeatures,
override val proguardOptions: ProguardOptions,
...
) : AndroidOptions(..) {
data class BuildFeatures(
val generateAndroidResources: Boolean = false,
val generateResValues: Boolean = false,
val generateBuildConfig: Boolean = false,
)
}
class AndroidLibraryOptionsBuilder : AndroidOptionsBuilder() {
..
var buildFeaturesBuilder = BuildFeaturesBuilder()
fun buildFeatures(block: BuildFeaturesBuilder.() -> Unit) {
buildFeaturesBuilder.apply(block)
}
override fun build(): AndroidLibraryOptions = AndroidLibraryOptions(
..
buildFeatures = buildFeaturesBuilder.build()
)
}
2 - Vamos atualizar nossa função applyAndroidLibrary()
para aplicar a decoração a partir do modelo:
internal fun Project.applyAndroidLibrary(androidLibraryOptions: AndroidLibraryOptions) {
applyAndroidCommon(androidLibraryOptions)
extensions.configure<LibraryExtension> {
...
buildFeatures {
androidResources = androidLibraryOptions.buildFeatures.generateAndroidResources
resValues = androidLibraryOptions.buildFeatures.generateResValues
buildConfig = androidLibraryOptions.buildFeatures.generateBuildConfig
}
}
}
3 - Nos passos a seguir, vamos desligar a geração do BuildConfig
por completo, incluindo o módulo app
. Já que é comum precisar do BuildConfig
no app
, vamos adaptar nossos modelos para trazer essa opção:
data class AndroidAppOptions(
..
val versionName: String,
val generateBuildConfig: Boolean,
override val proguardOptions: ProguardOptions,
..
) : AndroidOptions(..)
class AndroidAppOptionsBuilder : AndroidOptionsBuilder() {
..
var generateBuildConfig = false
override fun build(): AndroidAppOptions = AndroidAppOptions(
..
generateBuildConfig = generateBuildConfig
)
}
4 - Vamos adaptar o applyAndroidApp()
para que possa gerar o BuildConfig
:
internal fun Project.applyAndroidApp(androidAppOptions: AndroidAppOptions) {
..
extensions.configure<ApplicationExtension> {
..
buildFeatures {
buildConfig = androidAppOptions.generateBuildConfig
}
}
}
Adaptando build.gradle.kts
dos módulos
Iremos precisar atualizar o módulo designsystem
, já que temos uma pasta res
com os assets da aplicação.
Navegue até designsystem/build.gradle.kts
e inclua a geração dos recursos do Android:
androidLibrary {
buildFeatures {
// Isso irá identificar a pasta "res" e adicionar no classpath
generateAndroidResources = true
}
}
Desligando as funcionalidades no gradle.properties
1 - Vamos atualizar nosso gradle.properties
. Abra o arquivo e adicione as seguintes linhas:
android.library.defaults.buildfeatures.androidresources=false
android.defaults.buildfeatures.buildConfig=false
android.defaults.buildfeatures.aidl=false
android.defaults.buildfeatures.renderScript=false
android.defaults.buildfeatures.compose=false
android.defaults.buildfeatures.resValues=false
android.defaults.buildfeatures.viewBinding=false
2 - Já que estamos aqui, vamos aproveitar e aplicar outras configurações para otimizar nosso desenvolvimento com múltiplos módulos:
# -------Gradle--------
# Configurações de argumentos da JVM para a execução do Gradle.
# - Opções de desempenho e limites de memória.
org.gradle.jvmargs=-XX:+UseCompressedOops -XX:G1HeapRegionSize=16M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeLimit=20 -Xmx30g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Djava.awt.headless=true
# Executa tarefas em paralelo para melhorar o desempenho.
org.gradle.parallel=true
# Habilita o cache de tarefas do Gradle para reutilizar resultados de tarefas entre builds.
org.gradle.caching=true
# Configura o projeto apenas quando é necessário, melhorando os tempos de build.
org.gradle.configureondemand=true
# -------Kotlin--------
# Define o estilo de código Kotlin como o estilo oficial.
kotlin.code.style=official
# -------Android-------
# Habilita a utilização das bibliotecas AndroidX em vez das bibliotecas de suporte legadas.
android.useAndroidX=true
# Se verdadeiro, Jetifier converterá bibliotecas que não usam AndroidX para usá-lo.
# Não estamos utilizando nenhuma biblioteca de suporte, então podemos desligar esse passo
android.enableJetifier=false
# Habilita a geração da classe R em tempo de compilação para módulos de aplicativos.
android.enableAppCompileTimeRClass=true
# Desabilita a geração de recursos Android para bibliotecas.
android.library.defaults.buildfeatures.androidresources=false
# Desabilita a geração da classe BuildConfig.
android.defaults.buildfeatures.buildConfig=false
# Desabilita a compilação de AIDL.
android.defaults.buildfeatures.aidl=false
# Desabilita a compilação de RenderScript.
android.defaults.buildfeatures.renderScript=false
# Desabilita a funcionalidade Compose.
android.defaults.buildfeatures.compose=false
# Desabilita a geração de Valores de Recurso.
android.defaults.buildfeatures.resValues=false
# Desabilita a funcionalidade de View Binding.
android.defaults.buildfeatures.viewBinding=false
# Suprime os avisos de opções não suportadas.
android.suppressUnsupportedOptionWarnings=android.suppressUnsupportedOptionWarnings,android.enableAppCompileTimeRClass
# Habilita otimizações de recursos no projeto Android.
android.enableResourceOptimizations=true
3 - ⚠️ Importante: precisamos garantir que nosso gradle.properties
do Composite Builds estejam compartilhando a mesma configuração.
Caso o conteúdo do gradle.properties
do build-logic
seja diferente, o Gradle não compartilha os deamons
entre compilações.
Copie o conteúdo acima, e simplesmente cole no build-logic/gradle.properties
Sucesso!
Agora, nosso projeto assume que os módulos não terão nenhum recurso extra de compilação. Contudo, nossa plataforma se mantém flexível para atender módulos que necessitem de features específicas.
No próximo artigo, daremos um passo adicional na manutenção dos módulos, permitindo que nossa plataforma decore módulos "puro JVM", otimizando ainda mais o tempo de compilação quando possível.
Top comments (0)