Cómo compartir dependencias y compilar la configuración en kotlin-DSL | Autor: Keivan Shamlu | Diciembre de 2021
La reutilización de software puede acelerar la producción del sistema porque se puede reducir el tiempo de desarrollo y verificación. Podemos completar este trabajo una vez y reutilizarlo en lugar de hacer el mismo trabajo una y otra vez. Por lo tanto, siempre que escriba código que ya está en su proyecto, es mejor encontrar una forma de reutilizar ese código.
Cuando solo hay un módulo en nuestro proyectoTonelada, Realmente no nos ocupamos de reutilizar la lógica de Gradle (como configuración, tareas, complementos), porque solo hay un archivo build.gradle a nivel de módulo. Pero si nuestro proyecto es multimódulo, habrá varios archivos builds.gradle, su contenido es casi el mismo, por lo que podemos reutilizarlo.
contenido buildSrc
La construcción se considera incluida. Después de encontrar este directorio, Gradle compilará y probará automáticamente este código y lo colocará en la ruta de clase del script de compilación.Para compilaciones de varios proyectos, solo puede haber una buildSrc
El directorio, que debe estar ubicado en el directorio raíz del proyecto. buildSrc
Debería preferirse a los complementos de secuencias de comandos, porque es más fácil de mantener, refactorizar y probar el código. Documentación de Gradle
Como dice la documentación, en un proyecto de varios módulos, solo hay un módulo buildSrc y todos los módulos pueden acceder a él, porque está ubicado en la ruta de clase del script de compilación. Este parece ser un buen lugar para reutilizar nuestra lógica de Gradle, ¿verdad?
Cuando elegimos kotlin en lugar de groovy, hay demasiadas ventajas, sintaxis de plugin simplificada, configuración de retardo predeterminada, verificación en tiempo de compilación son algunas de ellas.Si ha utilizado groovy en su proyecto, compruébelo https://proandroiddev.com/better-dependencies-management-using-buildsrc-kotlin-dsl-eda31cdb81bf
Usamos algunas dependencias en build.gradle que es probable que se usen en otros módulos, por ejemplo, la biblioteca dagger utilizada como herramienta de inyección de dependencias en Android.Casi todos los módulos tienen esta dependencia, así que vamos a reutilizarla.
Agregue el módulo BuildSrc:
object DependencyInjection {object Dagger {
private const val version = "2.40.5"
const val runtime = "com.google.dagger:dagger:$version"
const val android = "com.google.dagger:dagger-android:$version"
const val android_support = "com.google.dagger:dagger-android-support:$version"
const val compiler = "com.google.dagger:dagger-compiler:$version"
const val android_support_compiler =
"com.google.dagger:dagger-android-processor:$version"
object Assisted {
private const val version = "0.8.1"
const val annotations =
"com.squareup.inject:assisted-inject-annotations-dagger2:$version"
const val processor =
"com.squareup.inject:assisted-inject-processor-dagger2:$version"
}
}
}
En este objeto kotlin, agrupamos todas las dependencias de dagger, y también reutilizamos su versión, por lo que todas las dependencias siempre tienen la misma versión, evitando así conflictos entre versiones de dependencias.
Usemos estas dependencias y agreguemos en la sección de dependencias de su build.gradle a nivel de módulo:
dependencies {
androidDeps.DependencyInjection.Dagger.apply {
implementation(runtime)
implementation(android)
implementation(android_support)
kapt(compiler)
kapt(android_support_compiler)
}
androidDeps.DependencyInjection.Dagger.Assisted.apply {
compileOnly(annotations)
kapt(processor)
}
}
Es fácil, ¿verdad?
Ahora, cada vez que desee actualizar la biblioteca de dagger, solo necesita cambiar la versión, y se actualizará todo el proyecto, ¡Grerreat!
Volvamos a hace 6 meses cuando estaba a cargo de revisar el desafío de código aceptado por el solicitante para el puesto de desarrollador abierto de Android de la empresa para la que trabajaba. Estoy familiarizado con kotlin-DSL, pero no tuve mucho tiempo para migrar mi proyecto. Hasta eso, muchos solicitantes lo usaron en sus desafíos de código, por lo que vi una gran cantidad de reutilización de dependencias, como lo hicimos anteriormente, pero nadie (literalmente) reutilizó la configuración, y luego, cuando les pregunté Por eso, dijeron: «Esto es imposible».
¡Bueno, es posible!
Retrocedamos en el tiempo, cuando usamos groovy y queremos reutilizar la configuración de {} android, creamos un archivo llamado android_feature_config.gradle Y ponlos todos en él:
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'android {
//configurations here
}
dependencies {
implementation KotlinLibraries.kotlin
}
Luego, en build.gradle a nivel de módulo:
apply from: '../android_feature_config.gradle'
Funciona a las mil maravillas…
Así que vamos a convertirlo a kotlin y veamos qué pasa. android_feature_config.gradle.kts:
plugins {
GradlePluginId.apply {
id(ANDROID_APPLICATION)
kotlin(ANDROID)
kotlin(KAPT)
}
}
android {//configurations here
}
Luego, en el nivel de módulo build.gradle.kts:
apply { from("../android_feature_config.gradle.kts") }
Después de sincronizar el proyecto, aparecerá este error:
¿Por qué sucede esto en kotlin-DSL en lugar de maravilloso? Debido a que kotlin-DSL es seguro para tipos, como dice la documentación:
Groovy DSL le permite hacer referencia a muchos elementos del modelo de construcción por su nombre, incluso si están definidos en tiempo de ejecución. Piense en configuraciones con nombre, conjuntos de fuentes con nombre, etc.Por ejemplo, puede obtener implementation
Configuración aprobada configurations.implementation
El .Kotlin DSL reemplaza esta resolución dinámica con modelos de acceso con seguridad de tipos que funcionan con los elementos del modelo proporcionados por el complemento.
Por lo tanto, cuando no esté en el script de compilación principal (configurar, inicializar, aplicar) o aplicar el complemento fuera del bloque de complementos (es decir, apply()
), no podrá acceder al acceso de seguridad de tipo DSL.
Muy bribón, ¿verdad?No te preocupes
Debemos implementar la extensión básica del complemento de Android Gradle y poner toda la lógica compartida allí para obtener más información sobre cómo extender el complemento de Android Gradle, consulte https://developer.android.com/studio/build/extend-agp
Entonces, nuestro primer paso es agregar la dependencia del complemento de Android en BuildSrc build.gradle.kts:
plugins {
`kotlin-dsl`
}
buildscript {
repositories {
google()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20")
}
}
repositories {
google()
mavenCentral()
}
dependencies {
implementation("com.android.tools.build:gradle:7.0.4")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.20")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20")
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
languageVersion = "1.3"
}
Luego, en extensions.kt en el módulo buildSrc:
fun DependencyHandler.implementation(depName: String) {
add("implementation", depName)
}
fun DependencyHandler.testImplementation(depName: String) {
add("testImplementation", depName)
}
fun DependencyHandler.androidTestImplementation(depName: String) {
add("androidTestImplementation", depName)
}
fun DependencyHandler.kapt(depName: String) {
add("kapt", depName)
}
fun DependencyHandler.compileOnly(depName: String) {
add("compileOnly", depName)
}
fun DependencyHandler.api(depName: String) {
add("api", depName)
}
Agrupemos todas las dependencias de Dagger:
fun DependencyHandler.daggerKotlin() {
DependencyInjection.Dagger.run {
implementation(runtime)
}
}
fun DependencyHandler.daggerAndroid() {
DependencyInjection.Dagger.run {
daggerKotlin()
implementation(android)
implementation(android_support)
kapt(compiler)
kapt(android_support_compiler)
}
}
fun DependencyHandler.daggerAssisted() {
DependencyInjection.Dagger.Assisted.run {
compileOnly(annotations)
kapt(processor)
}
}
Debería agrupar todas las dependencias como lo hice con dagger arriba, puede encontrar las dependencias agrupadas de mi proyecto aquí.
Luego agrupe todas las dependencias compartidas del módulo, por ejemplo, todos los módulos (kotlin, android) deberían depender de kotlin, dagger, test, arch, así:
fun DependencyHandler.baseDependencies() {
kotlinCoroutines()
daggerKotlin()
test()
arch()
}
fun DependencyHandler.baseAndroidDependencies() {
baseDependencies()
daggerAndroid()
testAndroid()
}
fun DependencyHandler.featureModuleBaseDependencies() {
androidDefault()
fragment()
lifeCycle()
arch()
naviagtion()
databinding()
daggerAssisted()
}
Aquí viene la parte importante, ampliamos BaseExtension:
val Project.android: BaseExtension
get() = extensions.findByName("android") as? BaseExtension
?: error("$name is not an android module")fun Project.androidLib(
fun Project.androidApp(appId: String) {
default: (DefaultConfig.() -> Unit)? = null
) {
android.run {
compileSdkVersion(AppConfig.compileSdk)
defaultConfig {
AppConfig.run {
versionCode = vCode
versionName = vName
minSdk = minimumSdkVersion
targetSdk = targettSdkVersion
testInstrumentationRunner = androidTestInstrumentation
default?.invoke(this@defaultConfig)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dataBinding {
isEnabled = true
}
}
dependencies {
baseAndroidDependencies()
}
}
androidLib {
applicationId = appId
}
}
fun Project.androidFeature(
default: (DefaultConfig.() -> Unit)? = null
) {
androidLib(default)
dependencies {
featureModuleBaseDependencies()
}
}
Creamos 3 funciones:
- androidlib (): Esta función es donde ponemos la configuración de Android, se aplicará a todos los módulos de Android (si la usamos en ese módulo), también contiene todas las dependencias de Android que agrupamos anteriormente.
- androidFeature (): Esta función se utiliza en nuestra función de Android, sí Biblioteca de Android Depende de la función
- Aplicación de Android (): Esta función se utiliza en el módulo de la aplicación, obtenga el id de la aplicación y se establezca en la configuración de la aplicación
En el archivo build.gradle.kts del módulo de aplicación:
plugins {
GradlePluginId.apply {
id(ANDROID_APPLICATION)
kotlin(ANDROID)
kotlin(KAPT)
}
}
androidApp(AppConfig.applicationId)
En la biblioteca de Android (también agrega un buildConfigField, que es opcional):
plugins {
GradlePluginId.apply {
id(ANDROID_LIBRARY)
kotlin(ANDROID)
kotlin(KAPT)
id(NAVIGATION_SAFEARGS_KOTLIN)
id(ANDROID_EXTENSIONS)
}
}
androidLib {
buildConfigField("String", "BASE_URL", ""https://jsonplaceholder.typicode.com"")
}
En la función de Android:
plugins {
GradlePluginId.apply {
id(ANDROID_LIBRARY)
kotlin(ANDROID)
kotlin(KAPT)
id(NAVIGATION_SAFEARGS_KOTLIN)
id(ANDROID_EXTENSIONS)
}
}
androidFeature()
Entonces solo necesitas llamar a una función ¡Todo está hecho!
Puedes ver el código fuente aquí
¡Gracias por leer!
Mia LinkedIn, GitHub
fuente
https://docs.gradle.org/current/userguide/kotlin_dsl.html
https://developer.android.com/studio/build/extend-agp