Theory:Building apps using Gradle
We hope you already got a basic understanding of what Gradle is and how to use it. In this topic, we will consider the basic structure of the build.gradle
file, and then build and run a small application. The knowledge you obtain here can be used for any JVM-based programming language supported by Gradle (e.g. Java or Kotlin).
注意
This article was written using Gradle 6.8.1. There may be some differences for other versions of Gradle. If you have troubles with this article, you can read the comments or just follow official Gradle doc (opens new window) instead of this.
# Initializing an application
We assume that you already have some experience with the terminal of your operating system and will interact with Gradle using it. First of all, create a new empty folder named what you want (e.g., demo
). In this folder, you need to invoke the gradle init
command to start initialising a new Gradle-based project. This command will show you a dialogue form to set up the project you need.
In this form, choose application
as the type of the project; Java or Kotlin as the implementation language; and org.hyperskill.gradleapp
as the project name if you would like to precisely follow our example (but it isn't required). For all other questions, you can choose their default options, since it doesn't matter now.
Below is an example of choosing options.
Select type of project to generate:
1: basic
2: application
3: library
4: Gradle plugin
Enter selection (default: basic) [1..4] 2
Select implementation language:
1: C++
2: Groovy
3: Java
4: Kotlin
5: Scala
6: Swift
Enter selection (default: Java) [1..6] 3
Split functionality across multiple subprojects?:
1: no - only one application project
2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2]
Select build script DSL:
1: Groovy
2: Kotlin
Enter selection (default: Groovy) [1..2]
Select test framework:
1: JUnit 4
2: TestNG
3: Spock
4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4]
Project name (default: demo): org.hyperskill.gradleapp
Source package (default: org.hyperskill.gradleapp):
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
After the initialisation is completed, the project structure will be the following:
.
├── app
│ ├── build.gradle
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── org
│ │ │ └── hyperskill
│ │ │ └── gradleapp
│ │ │ └── App.java
│ │ └── resources
│ └── test
│ ├── java
│ │ └── org
│ │ └── hyperskill
│ │ └── gradleapp
│ │ └── AppTest.java
│ └── resources
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
This structure includes a lot of files we have already considered (settings.gradle
, wrapper files, etc). The most important file called build.gradle
, which contains tasks and external libraries, is located within the app
directory. This folder exists because we've chosen application
as the type of the project and the folder represents our application.
There is also the src
directory inside app
. It contains two sub-directories main
and test
. This is a quite standard project structure when using Gradle. In our case, the package org.hyperskill.gradleapp
has some Java source code (App.java
).
If you chose Kotlin as the implementation language, the project structure will be the same except for Kotlin source code files (.kt
instead of .java
) and kotlin
folders instead of java
ones.
提示
Please note, it is a good practice for Java and Kotlin projects to include the name of your organization in the path to your source code files as a package name like org.hyperskill
. We follow this recommendation too.
# Running the application
If you look at the list of available tasks for managing the project using the command gradle tasks --all
, you will see that the list is fairly long. Here is a shortened version of it:
Application tasks
-----------------
run - Runs this project as a JVM application
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
...
2
3
4
5
6
7
8
9
There is the command run
which can start the application. To do it, just invoke the gradle run
command or use a Gradle wrapper script for your OS. This command will build and run the application. Here is an output example:
> Task :app:run
Hello World!
BUILD SUCCESSFUL in 623ms
2 actionable tasks: 1 executed, 1 up-to-date
2
3
4
5
As you can see, the autogenerated application can already display a welcome string. If you get a similar result, it means that everything is OK: your application works and Gradle can manage it!
If you look at the project structure again, you will see that it has some new files, including files with bytecode (App.class
, AppTest.class
). Actually, Gradle built and started the App.class
file when we invoked the run
command.
Now, let's consider the build file (build.gradle
for Groovy DSL or build.gradle.kts
for Kotlin DSL) thanks to which we can build our application successfully and run it using Gradle. This file specifies project structure and adds some tasks and external libraries to the project. We will not present the entire file here, only its main parts.
# Plugins
The plugins
section adds some plugins to extend the capabilities of the project: e.g., to add new tasks or properties.
plugins {
// Apply the application plugin to add support for building a CLI application
id("application")
// Apply the plugin which adds support for Java
id("java")
// Apply the plugin which adds support for Kotlin/JVM
id("org.jetbrains.kotlin.jvm")
}
2
3
4
5
6
7
8
9
10
Here, id
is a globally unique identifier, or name, for plugins. Core Gradle plugins are special in that they provide short names, such as "java"
or "application"
.
Basically, plugins for Kotlin and Java know how to build, package, and run tests on the project. The application
plugin facilitates creating an executable JVM application.
There is an alternative way to use a plugin in the project. It's more like a legacy way of applying plugins which is not widely used now, but just in case you see it somewhere, here it is:
apply plugin: "application" // for Groovy DSL
apply(plugin = "application") // for Kotlin DSL
2
There are many other plugins already available for you, and you can find them at the official Gradle Plugins page (opens new window). A large project can use dozens and hundreds of them. Gradle does not limit the maximum number of plugins used in a project.
# Repositories and dependencies
Usually you don't need to write your program from scratch – you use already written pieces of code, either yours or other developers'. This is where the dependency system comes in handy.
The repositories
section declares locations from which dependencies will be downloaded and added to the project.
repositories {
jcenter()
}
2
3
There are plenty of public repositories: JCenter, Maven Central, Google and others. Usually, a description of a dependency says which repository contains it.
The dependencies
section is used to add external libraries to the project. Gradle will automatically download them from the repositories and put in the archive with the application. Right now your dependencies
section should contain at least a testing library like JUnit
or something else, depending on your choice when the project was initialized.
dependencies {
// Use JUnit test framework.
testImplementation 'junit:junit:4.13'
// This dependency is used by the application.
implementation 'com.google.guava:guava:29.0-jre'
}
2
3
4
5
6
7
We will take a closer look at repositories and dependencies in the next topics.
提示
This is a standard Gradle build structure. You apply some plugins and specify dependencies for your project. This structure will be the same for any project managed by Gradle.
# Configurations for the application plugin
The auto-generated build.gradle(.kts)
file has a section which configures the application
plugin thanks to which the application runs with the gradle run
command as mentioned above.
application {
// Defines the main class for the application
mainClassName = "org.hyperskill.gradleapp.App"
}
2
3
4
The mainClassName
property defines a class with the entry point of the application. It allows us to run the application invoking the gradle run
command.
# Generating and running Jar archive
The classic way to run a JVM-based application is to use the java -jar
command. This command can be run without Gradle, you only need to have a JAR beforehand.
So let's build the JAR file for our application:
gradle jar
BUILD SUCCESSFUL in 748ms
2 actionable tasks: 2 executed
2
3
4
Now, the JAR file is in the app/build/libs
directory.
提示
If you want to clean the project folder from all generated artifacts, just run the gradle clean
command.
However, if you now try to run our generated application using the classic approach, there will be a problem:
java -jar app/build/libs/app.jar
no main manifest attribute, in app/build/libs/app.jar
2
The thing is that the application does not contain the Main-Class
attribute in the MANIFEST.MF
file. So, the JVM does not know the path to the entry point of the application.
To fix this we need to add the required attribute when generating an archive for the application. Just add the following declaration to the build.gradle(.kts)
file:
jar {
manifest {
attributes("Main-Class": "org.hyperskill.gradleapp.App") // for Groovy DSL
attributes("Main-Class" to "org.hyperskill.gradleapp.App") // for Kotlin DSL
}
}
2
3
4
5
6
This code adds the Main-Class
attribute to the manifest property of the jar task. See the manifest as a map of properties where we put our pair Main-Class -> Main
.
So, now when we execute gradle jar
followed by java -jar app/build/libs/gradleapp.jar
, everything should work as planned and you will see the output line Hello world!
. What is good about this way of running applications is that java -jar
command can be run without Gradle, you only need to have a JAR beforehand.
# Building the application
If you would like to generate a bundle of the application with all its dependencies and a script to start the application, use the gradle build
command.
BUILD SUCCESSFUL in 1s
7 actionable tasks: 7 executed
2
If everything is OK, Gradle will have produced the archive in two formats for you: app/build/distributions/app.tar
and app/build/distributions/app.zip
. Now, you can distribute your application!
# Conclusion
In this topic, you've learned how to generate Gradle-based applications with source code in Java or Kotlin as well as how to run this application using both gradle run
and java -jar
commands. You have also become familiar with the basic structure of the build.gradle(.kts)
file and got initial information about plugins, repositories, and dependencies. You will learn more about these things further on.