Engineering

12 March, 2019

Improve CI build times on your Android project

Our Android build times were reaching 20 minutes! That’s a lot of time lost in every pull request. And if you have more repositories set up...

António Valente

Software Engineer

How to improve Android build times using Travis CI - Coletiv Blog

At Coletiv we normally use Continuous Integration (CI) / Continuous Delivery (CD) in every project. It’s great once you have everything well configured, but to get there you have to spend too much time tweaking and testing your configurations in your CI service of choice. Believe me, I’ve had too many headaches with this, but finally, I was able to configure this in a way that I’m happy with.

The problem

There is a lack of good documentation and tutorials to help you set up your Android project. To start up, you’ll probably rely on examples given by other developers. At least that was the case for me, and that’s not a bad thing. I’m glad that there are other developers out there willing to help out, but most of the stuff that you find is either outdated or doesn’t fit your needs. Keep in mind that we’re currently using Travis-CI, but for what I could understand in my research, other services have the same problem.

With that in mind, our Android build times were reaching 20 minutes! That’s a lot of time lost in every pull request. And if you have more repositories set up in the same organization with no parallel builds, you’re probably blocking your colleagues’ builds, also wasting their time.

Trigger a build and pay close attention to the log until the end. Check what’s taking more time to run.

So the first thing you should do is identify what is causing this long build time on your project. Trigger a build and pay close attention to the log until the end. Check what’s taking more time to run. In our case, most of that time was spent on setting up the actually CI machine and on instrumentation tests, which are tests that run on physical devices and emulators.

Tip 1 — Check your config file for unneeded commands

Check this excerpt from our old config file:

android: components: - tools - platform-tools - tools - build-tools-25.0.3 - build-tools-24.0.3 - android-24 - addon-google_apis-google-24 - extra-google-google_play_services - extra-android-support - extra-google-m2repository - extra-android-m2repository - sys-img-armeabi-v7a-android-24 licenses: - android-sdk-preview-license-.+ - android-sdk-license-.+ - google-gdk-license-.+ - intel-android-extra-license.+

That seems like a lot of stuff needed only to build your Android app and to test it, right? The CI machine will download and install all that stuff! Do you really need all this?

Debug your build and check which commands you really need. Try to remove the components that you think you don’t need and test different configurations. Check if the build still passes. I know it’s not much to go on, but check out what we’ve achieved doing that.

env: global: - ANDROID_TARGET=android-28 - ANDROID_BUILD_TOOLS_VERSION=28.0.3 android: components: - tools - build-tools-$ANDROID_BUILD_TOOLS_VERSION - $ANDROID_TARGET - extra-google-google_play_services - extra-android-support - extra licenses: - '.+'

Now we only install the tools for our target Android version and removed most of the add-ons and extras that weren’t needed.

If you paid attention you’ll note that we removed all system images and you’re asking yourself, how am I supposed to run the emulator now? You simply don’t! And this leads us to the next tip.

Tip 2 — Don’t run emulators in your CI machine

It is not possible to run x86 android emulators on most cloud CI platforms, so you’ll have to use an arm-based emulator, but the last android version that supports it is android-25. That requires you to install and configure that android version, slowing down your build, not to mention the precious time lost just creating and starting the emulator.

Even though, if you’re interested in going that way, check out this thread on the Travis community forum, where they talk about adding KVM support, and this article, where multiple emulator versions are tested.

But what I really want to propose is running your instrumentation tests somewhere else. We’ve started doing it with Firebase Test Lab since we’re already using Firebase on our projects. You have 10 tests/day for free on virtual devices.

If you’re interested to know how we’ve got it working with Travis here is the relevant parts of our config file:

before_script: # Download and install gcloud cli to run firebase test commands - wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-218.0.0-linux-x86.tar.gz - tar zxf google-cloud-sdk-218.0.0-linux-x86.tar.gz google-cloud-sdk - echo n | ./google-cloud-sdk/install.sh - ./google-cloud-sdk/bin/gcloud auth activate-service-account --key-file dir-to-your-service-account-key.json - ./google-cloud-sdk/bin/gcloud config set project your-project - echo y | ./google-cloud-sdk/bin/gcloud components update - echo y | ./google-cloud-sdk/bin/gcloud components install beta script: - chmod +x gradlew - ./gradlew assembleDebug - ./gradlew assembleDebugAndroidTest # Run test on a Pixel 2 with Android 28 in english and in portrait - ./google-cloud-sdk/bin/gcloud beta firebase test android run --use-orchestrator --type instrumentation --app dir-to-your-apk/your-app.apk --test dir-to-your-test-apk/your-test-app.apk --device model=walleye,version=28,locale=en,orientation=portrait

Our instrumentation tests now run in less than 150 seconds. Previously that time alone wasn’t enough to just start the emulator. That’s an amazing improvement.

Tip 3 — Only run certain commands on a given branch or on a pull request

There are commands that you can run only on a certain branch. For instance, you might want to deploy your app only when there is a merge into master. Also, you certainly don’t want to run your instrumentations tests on every commit. You should only run them on pull requests. That way you can shave some precious minutes off of your build.

On Travis-CI you can do it like this:

- if [ $TRAVIS_BRANCH == 'master' ]; then your-command-here; fi - if [ $TRAVIS_PULL_REQUEST != false ]; then your-command-here; fi

Profit

Following these tips, our build times were reduced from over 20 minutes to just under 8 minutes! That’s amazing, right? Hope that you can achieve the same results! If you have any questions, drop a comment below and I will try to help you out.

Android

Software Development

Travis CI

Continuous Integration

Continuous Delivery

Join our newsletter

Be part of our community and stay up to date with the latest blog posts.

Subscribe

Join our newsletter

Be part of our community and stay up to date with the latest blog posts.

Subscribe

You might also like...

Go back to blogNext
How to support a list of uploads as input with Absinthe GraphQL

Engineering

26 July, 2022

How to support a list of uploads as input with Absinthe GraphQL

As you might guess, in our day-to-day, we write GraphQL queries and mutations for Phoenix applications using Absinthe to be able to create, read, update and delete records.

Nuno Marinho

Software Engineer

Flutter Navigator 2.0 Made Easy with Auto Router - Coletiv Blog

Engineering

04 January, 2022

Flutter Navigator 2.0 Made Easy with Auto Router

If you are a Flutter developer you might have heard about or even tried the “new” way of navigating with Navigator 2.0, which might be one of the most controversial APIs I have seen.

António Valente

Software Engineer

Enabling PostgreSQL cron jobs on AWS RDS - Coletiv Blog

Engineering

04 November, 2021

Enabling PostgreSQL cron jobs on AWS RDS

A database cron job is a process for scheduling a procedure or command on your database to automate repetitive tasks. By default, cron jobs are disabled on PostgreSQL instances. Here is how you can enable them on Amazon Web Services (AWS) RDS console.

Nuno Marinho

Software Engineer

Go back to blogNext