Android Remote Compilation

Guillermo Merino Jimenez
Making Tuenti
Published in
5 min readAug 14, 2020

How optimize Gradle tasks using remote compilation.
by Guillermo Merino Jiménez & David Pastor Herranz

Purpose

We are going to explain the reasons for using remote compilation for our Android Project and the problems/solutions we’ve had since we started using it. Instead of a tutorial post about how to configure Mirakle (which is explained well here), we will try to share our experience with around two years of (intense) use.

Understanding the problem

At Novum, we develop applications for companies in the Telefónica group. More precisely, we develop the application that lets users control their experience (manage their bills, support, Aura integration, etc…). It’s basically the main application for managing client accounts.

Our codebase is rather large. A git log –reverse in our Android repository goes back to 2011. Moreover, we use the same code for all of the product brands, so we need different productFlavors to setup the different variants. And we care about app size. We don’t want to include a library or resource that will be used in Brazil in our UK application, for example, so we use modules per feature with enabled/disabled implementations that are configured during build time.

This ends up creating a very complex matrix of combinations where certain Gradle tasks can take up to a few minutes to be executed.

Also, a big codebase comes with a lot of tests. We have around 4200 unit and integration tests and around 330 instrumentation tests.

The Solution

There are several ways to optimize Gradle tasks and a lot of documentation about it. You can still improve on potential problems and bottlenecks in your build configuration, but the solution has two big advantages.

  • It will significantly improve your build times
  • It will drastically reduce RAM usage on your development machines. So, only in economic terms, you can make up for the initial investment of a cloud/physical server with less maintenance or even less powerful computers.

We started with a proof of concept, hosted on an amazon virtual machine using the Mirakle plugin to sync our machines with the remote one. Our build times improved a lot, but we had two big issues.

  • We needed to access a private nexus for some of our libraries, but there was no way to connect that remote machine to our VPN.
  • Sync time was still too slow. We used an Amazon server in the US, because more powerful virtual machines were cheaper than in Europe.

To fix the security issue, we tested reverse tunnelling to temporally connect to our network when a build was in process. But we still had the network (and pricing) issue.

Happily, we were decommissioning some servers that used to handle our social network chat. They had 88 Intel(R) Xeon(R) Gold 6152 CPU @ 2.10 GHz cores and 500 GB of RAM.

With that new addition to the Android Team, and with some help from our Lifecycle Team, we were able to set up a dedicated server inside our network for executing our Gradle tasks. With the security problem solved, and a little tuning to distribute disk space among users, we were ready to go.

What is Mirakle?

Let’s start by explaining what Mainframer is. Mainframer is a tool based on rsync that offers you two ways of synching between your local machine and a remote one. Roughly, when you press the “compile” button it does the following.

  • It sends your source code to the remote machine.
  • It tells the remote machine to execute the compilation.
  • It syncs back the results to your local machine.

You end up trading compilation time for transfer time: If the remote machine is powerful enough, your main bottleneck will be transferring your source code and moving binaries back and forth from the remote machine.

And now comes Mirakle, which is is a fork of the Mainframer project modified to work with Gradle build systems. It’s a Gradle plugin and makes everything happening behind the scenes totally invisible to the upper layers. What does that mean? Android Studio won’t notice you’re compiling your code on another computer so you can continue working with your IDE as usual.

Configuring the Local Machine

Our developers have two scripts to enable and disable remote compilation in their machines.

Enable:

Disable:

Our mirakle.gradle looks like this

where

rsyncToRemoteArgs += ["-avzhP", "--no-p", "--exclude='.git'", "--exclude='.gradle'"]

We discovered that excluding those two folders was a good idea, since they’re big and can slow down your sync times.

downloadInParallel truedownloadInterval 2000

That was added in the most recent Mirakle releases. We found that it significantly speeds up download times, which is one of the most time consuming steps with a remote build.

CLI Mode

Excluding other binaries, except generated apks, could speed up the download step a lot, but it can break your AS integration since it relies on those generated files to work:

excludeRemote += ["**/build/generated/**"]excludeRemote += ["**/build/kotlin/**"]excludeRemote += ["**/build/tmp/**"]excludeRemote += ["**/build/intermediates/**"]

The numbers

Remote build using 500GB RAM, 88 Cores machine.

The performance improvement isn’t just the time improvement, we use 16GB RAM laptops to develop and we were having a lot of issues with RAM usage, making our machines very laggy while compiling.

Conclusions

High compile times are one of the most distracting processes a developer faces. And complex Android projects based on Gradle use to be slow. So we should care about compile time, not only to speed up the development process, but also improve our experience, avoiding distracting and stressful compilations of several minutes.

After two years using remote compilations we’ve seen several benefits:

  • Better compilation times (obviously)
  • Less laggy development computers (since we not run out of RAM)
  • We’ve improved the usage of Jenkins machines to validate our PRs. Since we’ve created simplified tasks to check the most common failures of a branch before being integrated. We prefer to run a 3 minutes tasks that checks unit tests, detekt and checkstyle to ensure that at least this, better than sending to a jenkins machine so other developer can use it.

Future improvements in our roadmap

Remote emulators

We’re experimenting with a suite of emulators hosted in the remote machine, to execute the instrumentation tests directly on remote. And accessing them using scrcpy. This is letting us to deploy quickly 3–4 emulators to test fastly a suite of instrumentation tests. Being able to connect remotely to them will let us debug as if we were using the emulators locally.

--

--