Build, test, and deploy Android apps

Azure Pipelines | Azure DevOps Server 2019 | TFS 2018 | TFS 2017

This guidance explains how to automatically build, test, and deploy Android apps.

Note

In Microsoft Team Foundation Server (TFS) 2018 and previous versions, build and release pipelines are called definitions, service connections are called service endpoints, stages are called environments, and jobs are called phases.

Note

This guidance uses YAML-based pipelines available in Azure Pipelines. For TFS, use tasks that correspond to those used in the YAML below.

Before this guidance, see Create your first pipeline.

Get started

You can build Android projects using Microsoft-hosted agents that include tools for Android. Or, you can use self-hosted agents with specific tools you need.

Sample code

To get started using a sample Android project, fork this repository in GitHub, or import it into Azure Repos or TFS:

https://github.com/MicrosoftDocs/pipelines-android

Your code

To get started using your own code, add the following YAML to a file named azure-pipelines.yml in the root of your repository. Change values to match your project configuration. See the Gradle task for more about these options.

# https://docs.microsoft.com/azure/devops/pipelines/languages/android
pool:
  vmImage: 'macOS-10.13'

steps:
- task: Gradle@2
  inputs:
    workingDirectory: ''
    gradleWrapperFile: 'gradlew'
    gradleOptions: '-Xmx3072m'
    publishJUnitResults: false
    testResultsFiles: '**/TEST-*.xml'
    tasks: 'assembleDebug'

Adjust the build path

Adjust the workingDirectory value if your gradlew file isn't in the root of the repository. The directory value should be relative to the root of the repository, such as AndroidApps/MyApp or $(system.defaultWorkingDirectory)/AndroidApps/MyApp.

Adjust the gradleWrapperFile value if your gradlew file isn't in the root of the repository. The file path value should be relative to the root of the repository, such as AndroidApps/MyApp/gradlew or $(system.defaultWorkingDirectory)/AndroidApps/MyApp/gradlew.

Adjust Gradle tasks

Adjust the tasks value for the build variant you prefer, such as assembleDebug or assembleRelease. For details, see Google's Android development documentation: Build a debug APK and Configure build variants.

Sign and align an Android APK

If your build does not already sign and zipalign the APK, add the Android Signing task to the YAML. An APK must be signed to run on a device instead of an emulator. Zipaligning reduces the RAM consumed by the app.

Important: We recommend storing each of the following passwords in a secret variable.
- task: AndroidSigning@2
  inputs:
    apkFiles: '**/*.apk'
    jarsign: true
    jarsignerKeystoreFile: 'pathToYourKeystoreFile'
    jarsignerKeystorePassword: '$(jarsignerKeystorePassword)'
    jarsignerKeystoreAlias: 'yourKeystoreAlias'
    jarsignerKeyPassword: '$(jarsignerKeyPassword)'
    zipalign: true

Test on the Android Emulator

Note: The Android Emulator is currently available only on the Hosted macOS agent.

Create the Bash Task and copy paste the code below in order to install and run the emulator. Don't forget to arrange the emulator parameters to fit your testing environment. The emulator will be started as a background process and available in subsequent tasks.

#!/usr/bin/env bash

# Install AVD files
# echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;android-27;google_apis;x86'

# Create emulator
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd -n xamarin_android_emulator -k 'system-images;android-27;google_apis;x86' --force

echo $ANDROID_HOME/emulator/emulator -list-avds

echo "Starting emulator"

# Start emulator in background
nohup $ANDROID_HOME/emulator/emulator -avd xamarin_android_emulator -no-snapshot > /dev/null 2>&1 &
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'

$ANDROID_HOME/platform-tools/adb devices

echo "Emulator started"

Test on Azure-hosted devices

Add the App Center Test task to test the app in a hosted lab of iOS and Android devices. An App Center free trial is required which must later be converted to paid.

# App Center Test
# Test app packages with Visual Studio App Center.
- task: AppCenterTest@1
  inputs:
    appFile: 
    #artifactsDirectory: '$(Build.ArtifactStagingDirectory)/AppCenterTest' 
    #prepareTests: true # Optional
    #frameworkOption: 'appium' # Required when prepareTests == True# Options: appium, espresso, calabash, uitest, xcuitest
    #appiumBuildDirectory: # Required when prepareTests == True && Framework == Appium
    #espressoBuildDirectory: # Optional
    #espressoTestApkFile: # Optional
    #calabashProjectDirectory: # Required when prepareTests == True && Framework == Calabash
    #calabashConfigFile: # Optional
    #calabashProfile: # Optional
    #calabashSkipConfigCheck: # Optional
    #uiTestBuildDirectory: # Required when prepareTests == True && Framework == Uitest
    #uitestStorePath: # Optional
    #uiTestStorePassword: # Optional
    #uitestKeyAlias: # Optional
    #uiTestKeyPassword: # Optional
    #uiTestToolsDirectory: # Optional
    #signInfo: # Optional
    #xcUITestBuildDirectory: # Optional
    #xcUITestIpaFile: # Optional
    #prepareOptions: # Optional
    #runTests: true # Optional
    #credentialsOption: 'serviceEndpoint' # Required when runTests == True# Options: serviceEndpoint, inputs
    #serverEndpoint: # Required when runTests == True && CredsType == ServiceEndpoint
    #username: # Required when runTests == True && CredsType == Inputs
    #password: # Required when runTests == True && CredsType == Inputs
    #appSlug: # Required when runTests == True
    #devices: # Required when runTests == True
    #series: 'master' # Optional
    #dsymDirectory: # Optional
    #localeOption: 'en_US' # Required when runTests == True# Options: da_DK, nl_NL, en_GB, en_US, fr_FR, de_DE, ja_JP, ru_RU, es_MX, es_ES, user
    #userDefinedLocale: # Optional
    #loginOptions: # Optional
    #runOptions: # Optional
    #skipWaitingForResults: # Optional
    #cliFile: # Optional
    #showDebugOutput: # Optional

Retain artifacts with the build record

Add the Copy Files and Publish Build Artifacts tasks to store your APK with the build record or test and deploy it in subsequent pipelines. See Artifacts.

- task: CopyFiles@2
  inputs:
    contents: '**/*.apk'
    targetFolder: '$(build.artifactStagingDirectory)'
- task: PublishBuildArtifacts@1

Deploy

App Center

Add the App Center Distribute task to distribute an app to a group of testers or beta users, or promote the app to Intune or Google Play. A free App Center account is required (no payment is necessary).

# App Center Distribute
# Distribute app builds to testers and users via App Center
- task: AppCenterDistribute@1
  inputs:
    serverEndpoint: 
    appSlug: 
    appFile: 
    #symbolsOption: 'Apple' # Optional. Options: apple
    #symbolsPath: # Optional
    #symbolsPdbFiles: '**/*.pdb' # Optional
    #symbolsDsymFiles: # Optional
    #symbolsMappingTxtFile: # Optional
    #symbolsIncludeParentDirectory: # Optional
    #releaseNotesOption: 'input' # Options: input, file
    #releaseNotesInput: # Required when releaseNotesOption == Input
    #releaseNotesFile: # Required when releaseNotesOption == File
    #isMandatory: false # Optional
    #distributionGroupId: # Optional

Google Play

Install the Google Play extension and use the following tasks to automate interaction with Google Play. By default, these tasks authenticate to Google Play using a service connection that you configure.

Release

Add the Google Play Release task to release a new Android app version to the Google Play store.

- task: GooglePlayRelease@2
  inputs:
    apkFile: '**/*.apk'
    serviceEndpoint: 'yourGooglePlayServiceConnectionName'
    track: 'internal'

Promote

Add the Google Play Promote task to promote a previously-released Android app update from one track to another, such as alphabeta.

- task: GooglePlayPromote@2
  inputs:
    packageName: 'com.yourCompany.appPackageName'
    serviceEndpoint: 'yourGooglePlayServiceConnectionName'
    sourceTrack: 'internal'
    destinationTrack: 'alpha'

Increase rollout

Add the Google Play Increase Rollout task to increase the rollout percentage of an app that was previously released to the rollout track.

- task: GooglePlayIncreaseRollout@1
  inputs:
    packageName: 'com.yourCompany.appPackageName'
    serviceEndpoint: 'yourGooglePlayServiceConnectionName'
    userFraction: '0.5' # 0.0 to 1.0 (0% to 100%)