Slide 1

Slide 1 text

Building Android Apps with Gradle

Slide 2

Slide 2 text

Gradle • Build automation tool, builds upon Maven and Ant • Uses build.gradle files for configuration ! PROTIP • Use the gradle wrapper http://www.gradle.org/

Slide 3

Slide 3 text

Gradle android plugin Build with `./gradlew clean assemble` ! Android Tasks • assemble • clean • connectedCheck • deviceCheck • check • build ! ./gradlew tasks --all http://tools.android.com/ tech-docs/new-build-system/ user-guide

Slide 4

Slide 4 text

Basic App Structure Project ├── app │ ├── build.gradle │ ├── keystore │ ├── libs │ │ └── *.jar │ └── src └── build.gradle

Slide 5

Slide 5 text

Top level build file Project ├── app │ ├── build.gradle │ ├── keystore │ ├── libs │ │ └── *.jar │ └── src └── build.gradle Add configuration options common to all sub-projects/modules. ! task wrapper(type: Wrapper) { gradleVersion = '1.8' }

Slide 6

Slide 6 text

Module build file Main application build file. It would contain information such as dependencies, plugins, etc. With the Android plugin, you can define additional properties for your application, such as package name, version code, etc. Project ├── app │ ├── build.gradle │ ├── keystore │ ├── libs │ │ └── *.jar │ └── src └── build.gradle

Slide 7

Slide 7 text

Sample build.gradle for an Android app https://github.com/f2prateek/android-device-frame- generator/blob/master/app/build.gradle

Slide 8

Slide 8 text

Basic App Structure My build.gradle is configured to pick up all jars in this folder. Use this for dependencies not available via published repositories. ! compile fileTree(dir: 'libs', include: '*.jar') . ├── app │ ├── build.gradle │ ├── keystore │ ├── libs │ │ └── *.jar │ └── src └── build.gradle

Slide 9

Slide 9 text

Sources Project ├── app │ ├── build.gradle │ ├── keystore │ ├── libs │ │ └── *.jar │ └── src └── build.gradle

Slide 10

Slide 10 text

Sources │ └── src │ ├── debug │ │ └── AndroidManifest.xml │ ├── instrumentTest │ │ ├── AndroidManifest.xml │ │ └── java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── res │ └── release │ ├── AndroidManifest.xml │ └── res │ └── values │ └── analytics.xml

Slide 11

Slide 11 text

Main Source Set │ └── src │ ├── debug │ │ └── AndroidManifest.xml │ ├── instrumentTest │ │ ├── AndroidManifest.xml │ │ └── java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── res │ └── release │ ├── AndroidManifest.xml │ └── res │ └── values │ └── analytics.xml The default source set. Stick to this if you want no build customization.

Slide 12

Slide 12 text

Instrument Tests Source Sets │ └── src │ ├── debug │ │ └── AndroidManifest.xml │ ├── instrumentTest │ │ ├── AndroidManifest.xml │ │ └── java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── res │ └── release │ ├── AndroidManifest.xml │ └── res │ └── values │ └── analytics.xml

Slide 13

Slide 13 text

Build Types │ └── src │ ├── debug │ │ └── AndroidManifest.xml │ ├── instrumentTest │ │ ├── AndroidManifest.xml │ │ └── java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── res │ └── release │ ├── AndroidManifest.xml │ └── res │ └── values │ └── analytics.xml The Android plugin gives you release and debug configurations for free. Simply create a source folder for each configuration and get started with basic build customization.

Slide 14

Slide 14 text

Build Types Some added tasks: • assembleDebug • assembleRelease ! ./gradlew assembleRelease OR ./gradlew aR Each configuration creates some appropriate corresponding tasks.

Slide 15

Slide 15 text

Android Configuration defaultConfig { minSdkVersion 14 targetSdkVersion 19 packageName "com.f2prateek.dfg" versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild versionName "${versionMajor}.${versionMinor}.${versionPatch}" buildConfig """\ public static final int VERSION_MAJOR = ${versionMajor}.; """ } def versionMajor = 2 def versionMinor = 0 def versionPatch = 0 def versionBuild = 1

Slide 16

Slide 16 text

Android Configuration This sample : • Defines minSdkVersion and maxSdkVersion • Defines package name • Defines version code and version name • Adds version name to BuildConfig (access it with BuildConfig.VERSION_MAJOR, no need for a Context!). Next version of the build tools will have more information out of the box, such as package name, version name, version code, etc.

Slide 17

Slide 17 text

Customizing Builds debug { packageNameSuffix ".debug" versionNameSuffix "-debug" } release { signingConfig signingConfigs.release }

Slide 18

Slide 18 text

Customizing Builds This sample : • Adds a suffix to the package name for debug build types • Adds a suffix to the version name for debug build types • Configures release build types to use a signing configuration that is shown in the next slide

Slide 19

Slide 19 text

Customizing Builds : Merging • All build types share code from the main source set • The manifest is merged into the app manifest • The code acts as just another source folder • The resources are overlayed over the main resources, replacing existing values

Slide 20

Slide 20 text

Customizing Builds : Uses • Permissions in debug mode only, but not in release mode • Custom implementation for debugging • Different resources for debug mode (for instance when a resource value is tied to the signing certificate)

Slide 21

Slide 21 text

Custom Build Types beta.initWith(buildTypes.release) beta { versionNameSuffix "-beta" } sourceSets.beta.setRoot('src/release') This creates a custom build type named ‘beta’, initializing it from the properties of the release build type. By default, it would have it’s source set under ‘src/beta’. Here I manually set the source set to be under ‘src/ release’. The only difference between the two versions in my case is that beta has a suffix in it’s version name.

Slide 22

Slide 22 text

Signing packages signingConfigs { release { storeFile file("keystore") storePassword "notYourRealPassword" keyAlias "keystore" keyPassword "notYourRealPassword" } } task askForPasswords << { def storePassword = new String(System.console().readLine("\n\$ Enter keystore password: ")) def keyPassword = new String(System.console().readLine("\n\$ Enter key alias password: ")) android.signingConfigs.release.storePassword = storePassword android.signingConfigs.release.keyPassword = keyPassword } ! tasks.whenTaskAdded { theTask -> if (theTask.name.equals("validateReleaseSigning")) { theTask.dependsOn "askForPasswords" } }

Slide 23

Slide 23 text

Signing packages This uses a file ‘keystore’ with an alias ‘keystore’. It asks for a password for both via the command line. Alternatively, you can make it pick up system properties, or pick them up in a file like signing.properties

Slide 24

Slide 24 text

Renaming the .apk file applicationVariants.all { variant -> apk = variant.packageApplication.outputFile; newName = apk.name.replace(".apk", "-v${defaultConfig.versionName}-${versionBuild}.apk"); newName = newName.replace("app", "${defaultConfig.packageName}"); variant.packageApplication.outputFile = new File(apk.parentFile, newName); if (variant.zipAlign) { variant.zipAlign.outputFile = new File(apk.parentFile, newName.replace("-unaligned", "")); } }

Slide 25

Slide 25 text

Renaming the .apk file Produces the following apk’s • com.f2prateek.dfg-beta-unaligned-v2.0.0-1.apk • com.f2prateek.dfg-beta-v2.0.0-1.apk • com.f2prateek.dfg-debug-unaligned-v2.0.0-1.apk • com.f2prateek.dfg-release-unaligned-v2.0.0-1.apk • com.f2prateek.dfg-release-v2.0.0-1.apk

Slide 26

Slide 26 text

Checkstyle (thank Jake Wharton!) apply plugin: 'checkstyle' ! checkstyle { configFile project.file('../checkstyle.xml') showViolations true } ! android.applicationVariants.all { variant -> def name = variant.buildType.name def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle checkstyle.dependsOn variant.javaCompile checkstyle.source variant.javaCompile.source checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir) checkstyle.exclude('**/BuildConfig.java') checkstyle.exclude('**/R.java') project.tasks.getByName("check").dependsOn checkstyle }

Slide 27

Slide 27 text

Checkstyle for library projects apply plugin: 'checkstyle' ! checkstyle { configFile project.file('../checkstyle.xml') showViolations true } ! android.libraryVariants.all { variant -> def name = variant.buildType.name def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle checkstyle.dependsOn variant.javaCompile checkstyle.source variant.javaCompile.source checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir) checkstyle.exclude('**/BuildConfig.java') checkstyle.exclude('**/R.java') project.tasks.getByName("check").dependsOn checkstyle }

Slide 28

Slide 28 text

Sample of Manifest in release, debug and main https://github.com/f2prateek/ android-device-frame-generator