Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Building Android Apps with Gradle

Building Android Apps with Gradle

See some of the features of the Android plugin for Gradle in action.

Prateek Srivastava

December 04, 2013
Tweet

More Decks by Prateek Srivastava

Other Decks in Programming

Transcript

  1. Gradle • Build automation tool, builds upon Maven and Ant

    • Uses build.gradle files for configuration ! PROTIP • Use the gradle wrapper http://www.gradle.org/
  2. 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
  3. Basic App Structure Project ├── app │ ├── build.gradle │

    ├── keystore │ ├── libs │ │ └── *.jar │ └── src └── build.gradle
  4. 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' }
  5. 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
  6. 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
  7. Sources Project ├── app │ ├── build.gradle │ ├── keystore

    │ ├── libs │ │ └── *.jar │ └── src └── build.gradle
  8. Sources │ └── src │ ├── debug │ │ └──

    AndroidManifest.xml │ ├── instrumentTest │ │ ├── AndroidManifest.xml │ │ └── java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── res │ └── release │ ├── AndroidManifest.xml │ └── res │ └── values │ └── analytics.xml
  9. 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.
  10. Instrument Tests Source Sets │ └── src │ ├── debug

    │ │ └── AndroidManifest.xml │ ├── instrumentTest │ │ ├── AndroidManifest.xml │ │ └── java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── res │ └── release │ ├── AndroidManifest.xml │ └── res │ └── values │ └── analytics.xml
  11. 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.
  12. Build Types Some added tasks: • assembleDebug • assembleRelease !

    ./gradlew assembleRelease OR ./gradlew aR Each configuration creates some appropriate corresponding tasks.
  13. 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
  14. 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.
  15. 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
  16. 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
  17. 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)
  18. 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.
  19. 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" } }
  20. 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
  21. 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", "")); } }
  22. 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
  23. 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 }
  24. 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 }