Travis CI Android example

Matti Mäki-Kihniä
6 min readNov 22, 2016

--

This example will show you how to setup Travis continuous integration on your Android project. The example will work even if you’ve implemented Constraint Layout in your project.

What this’ll cover:

  1. Setting up continuous integration on GitHub
  2. Setting up Travis builds
  3. Setting up automatic deployment to GitHub Releases

Note: Copying code from Medium doesn’t work really well, check out the code from here.

Travis will provide an image such as this to be put on your repository

Setting up continuous integration on GitHub

Once you have your repository created on GitHub click on Settings and Integrations & services. From the Add service drop down menu choose “Travis CI” then “Add service”.

Navigate to https://travis-ci.org/profile and click on the switch next to the repository that you’d like the Travis builds to be run with.

Note: once you’ve switched on Travis builds on your repository a build will be triggered every a commit or a pull request is made. Without a .travis.yml file the build will fail. Configuring the .travis.yml will be done on the next step.

Setting up Travis builds

You need to add a .travis.yml file into the root of your project. This file will tell how Travis handles the builds. You should check your .travis file on Travis WebLint before committing any changes to it.

You can find the very basic instructions for building an Android project from the Travis documentation.

At the beginning of your .yml file add the following parameters:

language: android
sudo: required
jdk: oraclejdk8

sudo requirement is added because a license needs to be manually added later.

So that cache isn’t always uploaded, add the following lines.

before_cache:
-rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
-rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
-$HOME/.gradle/caches/
-$HOME/.gradle/wrapper/

Specify the variables that are going to be used in the build. Set the ANDROID_BUILD_TOOLS and ANDROID_API to the same as specified in your projects build.gradle file.

env:
global:
- ANDROID_API=24
- EMULATOR_API=21
- ANDROID_BUILD_TOOLS=24.0.2
- ADB_INSTALL_TIMEOUT=5 # minutes

Then the components

android:
components:
- tools
- platform-tools
- build-tools-$ANDROID_BUILD_TOOLS
- android-$ANDROID_API
- android-$EMULATOR_API_LEVEL
- extra-google-m2repository
- extra-android-m2repository # for design library
- addon-google_apis-google-19 # google play services
- sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_API_LEVEL
- sys-img-armeabi-v7a-addon-google_apis-google-$EMULATOR_API_LEVEL
licenses:
- android-sdk-preview-license-.+
- android-sdk-license-.+
- google-gdk-license-.+

Before install part

before_install:
- mkdir "$ANDROID_HOME/licenses" || true
- echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" >
"$ANDROID_HOME/licenses/android-sdk-license"
- echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license"

- chmod +x gradlew
- ./gradlew dependencies || true

The bolded lines of the before_install can be skipped if you are not using Constraint Layout. See amouly’s answer on StackOverflow for an explanation.

The — ./gradlew dependencies || true is required if you get error please install missing components using the SDK manager… SOURCE.

Before_script defines the creation and starting of the Android emulator.

before_script:
- echo no | android create avd --force -n test -t android-21 --abi armeabi-v7a
- emulator -avd test -no-skin -no-audio -no-window &
- android-wait-for-emulator
- adb shell input keyevent 82 &

Script:

script:
- "./gradlew clean build connectedCheck -PdisablePreDex --stacktrace"

Command ./gradlew connectedCheck executes instrumentation tests located in src/androidTests/

Source: StackOverflow

Note: you’ll very likely want to add the following line to your gradle file:

lintOptions {
abortOnError false
}

If the abortOnError is true the build will fail if there is a lint error in the project.

Setting up automatic builds

Travis can upload your project builds directly to Github Releases or other providers. To be able to install an .apk file on an Android device the file needs to be signed. An unsigned .apk file can be installed on emulators.

A keystore is needed to create a signed .apk file. The key store can be created for example with Android Studio (Build -> Generate Signed APK -> Create new).

Note: remember the keystore password, alias and key password. They will be needed later.

Once the keystore file is created, place it in your project root. You’ll want to encrypt the file which is done with the travis command-line command:

travis encrypt-file keystore.jks

Running the command with the “ — add” argument will add the necessary decryption line to before install. Note: running the command with the argument on the “Bash on Ubuntu on Windows” will cause all line breaks to be deleted from the .travis.yml file.

After encrypting the file copy the generated script to the before_install section.

Running the command will leave the old keystore file in place and will also generate a keystore.jks.enc file. Remove the original keystore file from the project (keep it safe though) and keep the encrypted one.

The keystore password and key password need to be added to the .travis.yml file, as they will be used in the .apk signing process. The passwords need to be encrypted though.

travis encrypt storepass=key_store_password

Copy the generated values to your .travis.yml in the environmental variables section (env). Do the same for both the keystore password and the key password. Note: remember the variable names (storepass in this example) as they’ll be needed in the signing process.

The setup for the deployment is done in the before_deploy section of the .yml file. Here the jarsigner is used to sign the file with the keystore file provided, as well as to verify it.

before_deploy:
- cp $TRAVIS_BUILD_DIR/.keystore $HOME
- cd app/build/outputs/apk/
- jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore $HOME/keystore.jks -storepass $storepass -keypass $keypass app-release-unsigned.apk yourapp_keystore
# Verification
- jarsigner -verify app-release-unsigned.apk
- "${ANDROID_HOME}/build-tools/24.0.2/zipalign -v 4 app-release-unsigned.apk yourapp.apk"

First the .keystore file is copied into the $HOME directory, then jarsigner will use the .keystore file (key alias yourapp_keystore)to sign the “app-release-unsigned.apk”. The key store password ($storepass) and the key password ($keypass) are used. After the .apk is signed a verification is run. Afterwards zipalign is used to align the app-release-unsigned.apk and save it as yourapp.apk.

Next it’s necessary to set up the deploy section of the .travis

deploy:
provider: releases
file: yourapp.apk
skip_cleanup: true
on:
repo: githubUsername/Repository
tags: true
jdk: oraclejdk8
api_key:
secure: here goes the encrypted api key

You can generate the API key by going to your account settings on GitHub, Personal access tokens, Generate new token. Set the scope to public_repo and generate the token. Remember to copy the access token.

Encrypt the API key and add it to the deploy section under api_key.

travis encrypt apikey

Remember to check the Travis WebLint before committing your changes.

When a commit is tagged the Travis deployment will be triggered.

See here for the example .travis.yml file:

and example project:

https://github.com/harmittaa/travis-example-android

--

--