The New Area in Gradle: Version Catalog with Convention Plugins📢

Kaan Enes KAPICI
5 min readAug 3, 2024

--

Version Catalog

Herkese selamlar arkadaşlar 🖐

Çoğu Android projelerine baktığımız zaman Gradle dosyaları genellikle Groovy kullanılarak yazılmıştır. Ancak, Gradle’ın yeni özelliklerinden biri olan Version Catalog, proje bağımlılıklarını daha merkezi ve yönetilebilir bir şekilde tanımlamak için bizlere yeni bir yöntem sunmaktadır. Version Catalog’a geçmeden Version Catalog’a geçmenin avantajlarını ve dezavantajlarını konuşalım.

Konuya geçmeden önce bu yazıdaki anlatılan projenin tamamına şuradaki linkten ulaşabilirsiniz ➡️ https://github.com/kaaneneskpc/VersionCatalogWithConventionPlugins

Şimdiden hepinize iyi okumalar ☀

Avantaj✔️ & Dezavantaj❌

Okunabilirlik ve Yönetim Kolaylığı: Bağımlılıkların tek bir katalogda toplanması, gradle dosyalarının daha okunabilir ve yapılacak refactor işlemlerinin daha kolay hale gelmesini sağlar.

Tekrarlanan Kodun Azaltılması: Version Catalog, projede tekrarlanan bağımlılık tanımlarını azaltır. Aynı bağımlılığın farklı modüllerde tekrar tekrar tanımlanması yerine, tek bir katalogdan referans verilmesi yeterlidir.

Build Süresi: Version catalog için build süresini arttırıyor ya da azaltıyor şeklinde doğrudan etkisi olduğunu söylemek doğru olmaz. Projeden projeye farklılık gösterecektir. Bu süreçte gereksiz lib implementasyonlarından ve bağımlılıklardan kaçınmamız gerekiyor. Bunlar doğrudan olmasa da dolaylı yoldan build süremizi uzatabilir. Bunların haricinde Catalogun yanlış configure edilmesi de build süresini uzatabilir.

Avantajlara ve dezavantajlara baktığımıza göre artık nasıl entegre edebiliriz gelin hep beraber buna bakalım.

Entegrasyon 🖥️

Bir gradle Groovy, birisi Version Catalog, diğeri DSL farklı bir tanesi Convention Plugin kullanılarak aynı anda sync edilip proje build alabilir.

Bizler de geçişleri sağlamak için ve tek bir yerden yönetilebilir olması için Convention Plugin’i kullanacağız. Hazırsanız başlayalım ➡️

Bir örnek ile açıklayacak olursak Network katmanı implementasyonu için minimum 3–5 tane libi her modüldeki gradle a tek tek implemente etmek zorunda kalıyorduk. Version catalog sayesinde ilgili toml dosyasında bu impl’ler için bir bundle yazıp 8 satırda yapılan işlemi tek satıra indirgemiş olacağız ve her yerde tanımlamak yerine plugin sayesinde farklı modüllerde erişmek istediğimiz kütüphanelere tek bir yerden erişmiş olacağız.

Aşağıda Toml ve Version Catalog ile gösterimi yer almaktadır.

[bundles]
networks = [
"okhttp"
"gson"
"retrofit2"
"okhttp-loggingInterceptor"
]

[libraries]
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp"}
okhttp-loggingInterceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp"}
retrofit2 = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit"}
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson"}


implementation(libs.bundles.networks)

Burada ise Version Catalog öncesi gösterimi yer almaktadır.

implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.google.code.gson:gson:2.11.0")

Yukarıda görüldüğü gibi 4 satırda yapılacak işlemi tek satırda bitirmiş oluyoruz. Bu nedenle büyük projeler için Version Catalog yönetim ve bakım açısından işlerimizi daha da kolaylaştırıyor. Şimdi ise Convention Pluginler ile gelin hep beraber farklı modüllerde entegrasyonu nasıl tek bir yerden kontrol ediyoruz bakalım.

Öncelikle yeni bir modül oluşturmamız gerekiyor. Bunu’da resimde gördüğünüz gibi Sol üst tarafta Project yazıyor. Bu sizde Android olabilir project yapmamız gerekiyor. Sonrasında VersionCatalag’a sağ tıklayıp “Create New Module” yazan yere tıklayınca böyle bir yer geliyor. İsimlendirmeyi istediğiniz şekilde yapabilirsiniz. Finish dedikten sonra ise yeni modülümüz oluşmuş oluyor.

Create New Module

Bu modülü ekledikten sonra settings.gradle.kts dosyası oluşuyor onun içinde ufak bir değişiklik yapmamız gerekiyor. include(“:build-logic”) olarak en alta geliyor lakin pluginlerle beraber kullanacağımız için yukarıya taşımamız ve includeBuild(“build-logic”) olarak değiştirmemiz gerekiyor.

pluginManagement {
includeBuild("build-logic")
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}

rootProject.name = "VersionCatalog"
include(":app")

Sırada ise build logic içinde yeni bir modül ekliyoruz-burdaki Class ismi bir tık önemli- çünkü içine yazacağımız extensionlar olacak. Sonrasında settings.gradle.kts içinde eklenecek olan include(“:build-logic:convention”) alanını silmemiz gerekecek çünkü bunu burda kullanmayacağız.

Second Module

Sonrasında ise build logic modülümüz içine yeni bir file ekliyoruz (settings.gradle.kts adında). Sebebi ise bu modülde de Version Catalog kullanmamız için gerekli. İçerisinde bulunması gereken kodu aşağıya ekliyorum.

dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

rootProject.name = "build-logic"
include(":convention")

Sonrasında libs.toml’a 2 tane plugin eklememiz gerekiyor ki convention modülündeki build.gradle.kts’de işlemlerimize devam edebilelim. Convention modulündeki kts dosyasının içeriğini de aşağıya ekledim. Toml’daki 2 libraryi dependencies olarak eklememiz gerekiyor.

[libraries]

android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }

//build.gradle.kts -> convention module
plugins {
id("java-library")
alias(libs.plugins.jetbrains.kotlin.jvm)
}

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

dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
}

Sonrasında ise convention module içine 2 tane class ekleyeceğiz birincisi Version Catalogları çekebileceğimiz bir extension diğeri de bunları apply edeceğimiz bir pluginimiz.

Packages
// NetworkConfigConventionPlugin

import com.kaaneneskpc.convention.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies

class NetworkConfigConventionPlugin : Plugin<Project> {

override fun apply(target: Project) = with(target) {
dependencies {
add("implementation", libs.findLibrary("okhttp").get())
add("implementation", libs.findLibrary("okhttp-loggingInterceptor").get())
add("implementation", libs.findLibrary("retrofit2").get())
add("implementation", libs.findLibrary("gson").get())
}
}

}

//ProjectExtensions


import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.getByType

val Project.libs
get(): VersionCatalog = extensions.getByType<VersionCatalogsExtension>().named("libs")

Artık Pluginimizi oluşturduk. Sıra diğer modüllerde kullanmaya geldi. Öncelikle kullanabilmemiz için convention’ın kendi build.gradle.kts içerisinde gradlePlugin’i tanımlamamız gerekiyor.


//build.gradle.kts
plugins {
`kotlin-dsl`
id("java-library")
alias(libs.plugins.jetbrains.kotlin.jvm)
}

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

dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
}

gradlePlugin {
plugins {
register("networkConfig") {
id = "kaaneneskpc.network.config"
implementationClass = "NetworkConfigConventionPlugin"
}
}
}

//libs.versions.toml

[plugins]
kaaneneskpc-network-config = { id = "kaaneneskpc.network.config", version = "unspecified" }

Burada kendi oluşturduğumuz plugini tanımlamamız gerekiyor kullanabilmemiz için. Register içindeki id istediğimiz şekilde olabilir ancak implementationClass Plugin Class’ımıza hangi ismi verdiysek o olmalı. Sonrasında ise toml dosyası içinde plugin altına kendi pluginimizi eklememiz gerekiyor. Buradaki id gradle içindeki id ile uyuşmalı ve version olmadığı için unspecified olarak tanımlamamız gerekir.

Şimdi ise app module içinde bir NetworkManager objesi tanımlayalım ve ilgili kütüphaneleri kullanmaya başlayalım.

Network Manager

Kullanmamız için build.gradle.kts(:app) içine kendi oluşturduğumuz plugini ekliyoruz.

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.kaaneneskpc.network.config)
}

Vee sonuç 🎉. Artık farklı bir modülde tanımladığımız plugin sayesinde kütüphaneleri teker teker tekrardan implement etmeden kullanabilir hale geldik.

Final Image

Android versiyon katalogları, geliştiricilere projelerinde kullanılan bağımlılıkları ve eklentileri daha etkili bir şekilde yönetme olanağı sunar. Bu kataloglar, projelerin bakımını ve güncellemelerini kolaylaştırırken, versiyon uyuşmazlıklarını da minimuma indirir.

Modern Android geliştirme süreçlerinde versiyon katalogları, ekiplerin işbirliğini artırmakta ve kod tabanının daha sürdürülebilir olmasını sağlamaktadır. Geliştiriciler, versiyon kataloglarını kullanarak projelerinde daha düzenli ve kontrollü bir yapı kurabilir, böylece proje kalitesini ve güvenliğini artırabilirler.

Sonuç olarak, Android versiyon katalogları, hem bireysel geliştiriciler hem de büyük ekipler için önemli bir araç haline gelmiştir ve Android ekosisteminin vazgeçilmez bir parçası olmaya devam edecektir.

Beni dinlediğiniz için teşekkür ediyorum :) Bir sonraki yazımda görüşmek üzere. Hoşçakalın 🖐

Kaynakça:

--

--

Kaan Enes KAPICI

Hi everybody, I’m Kaan. Senior Application(Android) Engineer at @TurkTelekom/Innova - Ex @QnbFinansbank Love cats and dogs.🐶🐈. Writing whatever I want..