Introduction
- This series is going to be dedicated to the basic to Android development. Join me and let us try to build and understand some cool stuff. This post will be on understanding why our build files look the way that they do and how they behave. If you are looking for a more specific piece of our build files like,
compileSdkVersion 30
then please wait for my next post. I will leave a link HERE once it is done.
what is Gradle?
- Gradle is a build automation tool. It is thanks to Gradle that all of our Android files, classes, resources and everything else gets compiled together and is able to run on the emulator or a hardware device. Gradle does all this goodness through the power of its build files and plugins, which we will talk about shortly.
What is Groovy?
Groovy is a programming language built to integrate smoothly with Java. Groovy allows us to be more expressive with our Java and lets our Java code look more like python. The origin of Groovy comes from the creator looking at python documentation and realizing Java was lacking in its expressiveness. Groovy is the programming language that we use in the Gradle build files.
Module level build files
means build files that will only affect our application and not anything specific to Gradle, things like the Android SDK version.Now that we have a loose understanding of what is going on, lets jump into the code from the
build.gradle
file.
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
defaultConfig {
applicationId "com.example.criminalintent"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
println(getDelegate())
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
What is happening?
- The first thing that we should talk about is the syntax because it looks a little like JSON but it is actually a bunch of method calls in Gradle's domain specific Language(DSL).
What is a DSL?
- It is a programming language that is a specialized to run on a specific domain. Now what is a DSL in relation to Gradle? Well Gradle gives its own specialized way to code in its build files and we can do so with either Kotlin or Groovy(We chose Groovy).
Lets talk plugins
- Before we move on lets talk about what a plugin is. A plugin is a unit of work in Gradle, meaning it allows Gradle to do stuff. In reality Gradle can't actually do that much by it's self, it is because of plugins that Gradle can do stuff like compile code and link files together. The Android plugin that is automatically provided to us is what allows Gradle to compile, link and run our files on a hardware device. Without that plugin we could not properly run our app
QUICK NOTE ABOUT THE PLUGIN SYNTAX
- I would like to point out that the plugin code that we see below is very different from the rest of the code in the file. The plugin code block is special, it is called the
plugin DSL
(Domain specific Language). When we declare plugins we use this special syntax it allows Gradle and us to quickly determine what plugins are being used. I can not stress it enough, the syntax looks similar to the rest of the code but it is not!! From this point on I will be referring to the plugin code block as theplugin DSL
.
plugins {
id 'com.android.application'
}
- There are different kinds of plugins but the plugin that we defined in the plugin DSL is called a
Binary plugin
. Binary plugins are defined by there unique id, which is a globally unique identifier. Without this id Gradle does not know which plugin we are referring to.
GROOVY WARNING!!!!!
- This is where things are going to get groovy with Groovy, if you are new to Android you can avoid this section. We are going to be talking about the hidden syntax in our build file, minus the plugin DSL. This syntax is all thanks to Gradle's DSL that is provided to us by Groovy. Now I call it a hidden syntax because if you are unfamiliar with the Gradle DSL(which is just a specific form of Groovy ) you can easily be mislead to believe that we are just declaring properties.
android {
compileSdkVersion 30
println(getDelegate())
defaultConfig {
applicationId "com.example.criminalintent"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
println(getDelegate())
}
}
- Normally when seeing something like this you would think we are defining properties for the build system. But thanks to the syntax of Groovy we are actually making a bunch of method calls to specific objects that change with each inner code block. If you want to see what these mysterious objects are, call
println(getDelegate())
in each code block (notice println(getDelegate()) does not work in the plugin DSL ). The print code is just more Groovy, so feel free to read the documentation and get more comfortable with the language. If you look in the Gradle log files you will notice a bunch of weird objects being printed to the console, this is our doing. Those objects aredelegate
objects.
What is a Delegate?
- A delegate is just a third party object that is used to find method calls and properties when they can not be found in the immediate scope. Now delegate objects are important because in Groovy a closure is even more important.
What is a closure?
- In Groovy a closure is an open anonymous, block of code that can take arguments, return a value and be assigned as a variable. Long story short, its a function. Another important note is that each closure has a delegate object, which Groovy then uses to look up variables and method references that are not in the local scope of the closure. Please read that sentence a few more times, it is a important step in the Gradle DSL.
What does this all mean??
- Now that we have a solid understanding of what a delegate object and what a closure is, we can start talking about the hidden syntax of the
android{ }
. Despite it not looking like it, that syntax is actually a Groovy method call and the method takes a closure as a parameter. soandroid{ }
becomesandroid(closure){}
and everything declared inside the android{} block is a method invocation that looks to the delegate object of the closure for reference. Every statement followed immediately by a {} follows this structure.Here is a link that goes over this idea more thoroughly. Ok, where does this delegate object come from and why does it know so much about Android? It is provided to us via the Binary plugin that we define inside theplugin DSL
. We can even prove this by deleting the android plugin and syncing our build files. You will notice a error that states,Could not find method android()
, this proves 2 things: 1) android{} is indeed a method and 2) the android plugin is what makes everything possible.
Before you go
- I tried my best to make a quick, easy and digestible blog post about what happens in our build file. However, I am still new to android and I know I probably missed a few things. So please do read the documentation on Groovy closures, delegate objects and Gradle documentation.
References
Conclusion
- Thank you for taking the time out of you day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.
Top comments (0)