Simplify Your Android Builds: A Guide to Convention Plugins
Learn How to Create and Use Custom Gradle Plugins
Photo by Birger Strahl on Unsplash
What’s a Convention Plugin
A convention plugin is a structured approach to organizing Gradle files, which can be used as a plugin within the Gradle
module.
Benefits
Reusable: Plugins can be reused across multiple Gradle modules, enhancing consistency and efficiency.
Easy to Maintain: Complex build logic can be developed by extending or composing other build logic and can be encapsulated into dedicated plugins (such as
ArtifactDeploymentPlugin
,AndroidLibraryPlugin
, andAndroidComposePlugin
, etc.) for ease of use.Easy to Consume: By applying plugins, users can easily add new features to their build process without having to write complex code from scratch again.
Testable Build Logic: The build logic can be tested using TestKit to verify its behavior. (Yet to try this)
How to Add This Convention Plugin
Step 1: Create the build-logic
Folder
Create a separate
build-logic
folder to organize your convention plugin code and separate it from the rest of your project’s code.At the root level, create a
settings.gradle.kts
file with the following content:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml")) // Create this file if not present.
}
}
}
rootProject.name = "build-logic"
// include(":convention") // Enable this line only after step 2
Inside the project’s settings.gradle.kts
, add includeBuild(“build-logic”)
within the pluginManagement
block.
pluginManagement {
includeBuild("build-logic") // Include the build-logic
repositories {
// ...
gradlePluginPortal()
}
}
// ...
Step 2: Create the Build Convention Module
Add a new Java/Kotlin module under
build-logic
and name it asconvention
. In this example, the package name iscom.droidstarter.convention
.Ensure that any entry like
include(“:build-logic:convention”)
in the root settings.gradle.kts file is removed (Studio will add this entry automatically when a new module is created).Replace the script in the
convention
module’sbuild.gradle.kts
with:
plugins {
`kotlin-dsl`
}
group = "com.droidstarter.buildlogic" // Package name for the our plugins
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
}
gradlePlugin {
plugins {
// Will add in next step
}
}
- Add the following dependencies to
libs.versions.toml
:
[versions]
androidGradlePlugin = "8.0.2" # use latest version
kotlin = "1.9.22" # use latest version
[libraries]
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
- Add
include(“:convention”)
to thesettings.gradle.kts
file in thebuild-logic
module, then perform a Gradle sync.
Step 3: Create a Custom Gradle Convention Plugin
First, set up a basic convention plugin named
AndroidApplicationComposeConventionPlugin
. This plugin will centralize
all the build configurations needed for setting up an Android project with Jetpack Compose.Implement
Plugin<Project>
to define and apply custom configurations.
class AndroidApplicationComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
println("*** AndroidApplicationComposeConventionPlugin invoked ***")
// Additional configuration here...
}
}
- Register the plugin inside the build-logic’s
build.gradle.kts
, making it accessible for use in other modules:
plugins {
`kotlin-dsl`
}
// ...
gradlePlugin {
plugins {
create("androidApplicationCompose") {
id = "com.droidstarter.convention.application.compose" // This is the id we used to resolve our plugin.
implementationClass = "com.droidstarter.convention.AndroidApplicationComposeConventionPlugin"
}
}
}
- Apply the newly created plugin to your app module or any Android application module.
plugins {
id("com.droidstarter.convention.application.compose")
}
- Perform a Gradle sync, and you should now see the following logs in the build window:
**** AndroidApplicationComposeConventionPlugin invoked ****
Step 4: Configure the Plugin for Android App with Jetpack Compose Build Logic
- Apply the Android and Kotlin plugins within your convention plugin so that when it is applied, those plugins are automatically included. This allows you to remove them from the app module’s
build.gradle.kts
.
class AndroidApplicationComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
println("*** AndroidApplicationComposeConventionPlugin invoked ***")
with(target) {
with(pluginManager) {
apply("com.android.application") // Include android application plugin
apply("org.jetbrains.kotlin.android") // Ensure project build.gradle declared this plugin
}
}
}
}
Configure common settings like
compileSdk
,minSdk
,targetSdk
,sourceCompatibility
,targetCompatibility
, andkotlinJvmTarget
.Set up Jetpack Compose configuration, including enabling compose features and adding necessary dependencies.
class AndroidApplicationComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
println("*** AndroidApplicationComposeConventionPlugin invoked ***")
with(target) {
with(pluginManager) {
apply("com.android.application")
apply("org.jetbrains.kotlin.android") // Ensure project build.gradle declared this plugin
}
extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this)
defaultConfig.targetSdk = 34
}
}
}
}
internal fun Project.configureKotlinAndroid(commonExtension: CommonExtension<*, *, *, *>) {
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
commonExtension.apply {
compileSdk = 34
defaultConfig {
minSdk = 29
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
compose = true
}
composeOptions {
// Add androidxComposeCompiler in toml
kotlinCompilerExtensionVersion = libs.findVersion("androidxComposeCompiler").get().toString()
}
dependencies {
// Add androidx-compose-bom in toml
val bom = libs.findLibrary("androidx-compose-bom").get()
add("implementation", platform(bom))
add("androidTestImplementation", platform(bom))
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
}
}
- Add the following dependencies to
libs.versions.toml
:
[versions]
androidxComposeCompiler = "1.5.10" # use latest version
androidxComposeBom = "2024.02.01" # use latest version
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
Conclusion
If the steps outlined above feel overwhelming, you can simplify the process by cloning the Android template repository available at my GitHub.
This repository already contains the essential build configurations and boilerplate setups, including Hilt, JaCoCo test reporting, and plugins for Android Library and Jetpack Compose, among other features.
Top comments (0)