Continuous Integration for Android

Learn how to use Continuous Integration for Android to be sure you have fully-verified and battle-tested code on the master branch. By Prashant Barahi.

4.8 (11) · 1 Review

Download materials
Save for later

The master branch in a repository is generally the most stable of all the branches. Developers should be able to make a production-ready (or equivalent) build out of it — which means the master branch has to have fully-verified and battle-tested code. But how can you be sure that’s the case?

That’s where CI comes in! CI, short for continuous integration, is a development practice in which each member of a team frequently merges their codes into the main repository branch. Each integration triggers an automated build and test workflow, allowing the team to detect and fix problems as early as possible.

In this tutorial, you’ll learn how to implement continuous integration for Android in an app called Simple Calculator.

In the process, you’ll learn to:

  • Implement CI in your workflow.
  • Use GitHub Actions.
  • Integrate testing frameworks to compliment the CI workflow.
  • Use code coverage tools like JaCoCo — and learn why they’re important.

Getting Started

Click the Download Materials button at the top or bottom of the page to download the starter project. Launch Android Studio 4.0 or later and select Open an existing Android Studio project, then navigate to and open the starter project’s folder.

Build and run the app, then take some time to use it and then familiarize yourself with its code.

How Simple Calculator works

Now, take a look at the starter project. It contains the following files:

List of files in the project structure

For example, clicking on any numerical or decimal button appends a tag that holds the value that corresponds to the input field of the calculator.

  • Calculate.kt is an interface that all the singleton classes in the operators package implement.
  • MainActivity.kt handles the UI interactions. Each button inside the ConstraintLayout has a common View.OnClickListener and a tag associated with it. When the user clicks a button, it invokes the onClick(), which uses the tag of the clicked view to decide how to handle it.
  • CalculatorEngine.kt is the core of the app, driving the calculation. Based on the input operator, it delegates the calculation to corresponding classes and updates the result with the output of the calculation.
Note: The project also contains some unit tests and instrumentation tests. The instrumentation tests are written using Kaspresso. Tests are vital — continuous integration is ineffective without them.

Start the emulator and run both unit and instrumentation tests. Don’t forget to turn off device animations before running instrumentation tests since these often depend on the UI to work properly.

Take some time to explore the files inside app/build, which will be important in the upcoming sections.

Now, it’s time to start implementing continuous integration for Android.

Understanding Continuous Integration

Since the master branch has to be stable at any given time, no developer should push their commits directly to that branch. When working on a new feature, you must create a new branch from the master then work on that branch.

When you’re done, you pull changes from the main branch into yours, resolve any merge conflicts then push that branch to the project repository. Multiple developers working on the same repository should all follow that same pattern.

To keep your branch updated, you need to pull changes from the main branch frequently. This also saves you from huge merge conflicts.

When you push your changes to your repository, you trigger the CI workflow that runs the test cases. You can only merge your commit to the master after the code passes the tests.

If the tests fail, you have to hunt for the errors using the CI logs, fix them and repeat those steps. This ensures that only tested and working code gets to the master branch.

Continuous integration workflow

So now that you understand the theory, it’s time to look at how to put CI into practice.

Workings of a Continuous Integration System

Most CI providers use virtual machines and/or lightweight abstractions called containers. Containers allow a developer to package an application and all its required dependencies and deploy it as one package. For example, you could package a web application that runs on Java with the Java Runtime Environment.

A popular containerization tool is Docker. When you use Docker, you distribute the containers as a Docker image.

For this tutorial, you’ll use a Docker image to spawn the Ubuntu OS without an user interface. Inside the container, you’ll use apt-get to set up necessary tools like Git and Gradle. With them installed, you’ll be able to clone your Android project from the repository, build the project and a lot more. The caveat is that since there is no interface, you’ll have to use the CLI (Command Line Interface).

In the next step, you’ll go behind-the-scenes of what happens inside a CI machine.

Working With a Docker Container

Follow the official guides to set Docker up on your system. Once you install it, you’ll be able to run Ubuntu on top of your OS. Just open a terminal window and enter the command below:

docker run -it ubuntu:latest

With this command, you download the ubuntu image with the latest tag from DockerHub, if it’s not available locally. You then run it in interactive mode — that is, you get access to the container’s terminal.

Note: Docker isn’t only useful for running OSes. You can also use it to run databases like MySQL, PostgreSQL and web applications. You can even create and distribute your own images.

Great! You now have an Ubuntu OS running on top of your OS, ready to run your commands.

Running Docker

Note: Use the docker images command to list all the downloaded images and docker ps to list the running containers. You can find other useful Docker commands in our tutorial, Getting Started with Docker.

Next, you’ll enter the following command:

apt-get update
apt-get install git-all

The first command makes sure all your packages are currently updated and the second installs Git in your docker container.

To install Gradle, follow Linuxize’s directions on installing Gradle on Ubuntu.

The bottom line is that you’ll be presented with a lightweight OS and all you need to do is provide the commands for it to execute.

However, since you’ll be using some third-party providers for this tutorial, you won’t need to deal with Docker directly and most likely won’t need to install standard tools like Git. But the tools like Gradle and JDK (Java Development Kit) are unlikely to be provided. If so, you need to manually set these up in the container.

In the next section, you’ll learn how to use GitHub’s automation tool: GitHub Actions.