Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Level Up Your Android Build

Level Up Your Android Build

With the release of the new Gradle-based Build System, Android finally has a flexible and highly customizable build toolchain that allows for tweaks at all stages of development, from code generation to deployment on the Google Play Store.

We assume that by now the community is familiar with the basic capabilities of the Android build tool. This talk aims to provide some more advanced tricks and techniques of how to extend and customize your build employing the wide and growing range of Gradle plugins.

Talk given at Droidcon Paris 2015 & DevFest Berlin 2015
Novoda Berlin - Volker Leck & Florian Mierzejewski

Recording: https://www.youtube.com/watch?v=513e57e7jlo

Florian Mierzejewski

November 13, 2015
Tweet

More Decks by Florian Mierzejewski

Other Decks in Programming

Transcript

  1. app/build.gradle apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion '23.0.1'

    defaultConfig { ... } buildTypes { release { ... } } } dependencies { ... }
  2. Relevant Build Files ├── app │ └── build.gradle ├── build.gradle

    ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle
  3. Using a Gradle plugin /build.gradle buildscript { repositories { jcenter()

    } dependencies { classpath 'com.android.tools.build:gradle:1.3.1' classpath 'com.novoda:gradle-android-command-plugin:1.5.0' } } app/build.gradle apply plugin: 'com.novoda.android-command' $ gw runDebug https://github.com/novoda/gradle-android-command-plugin
  4. Dependencies apply plugin: 'com.neenbedankt.android-apt' dependencies { apt 'com.google.dagger:dagger-compiler:2.0' compile 'com.google.dagger:dagger:2.0'

    compile 'com.google.guava:guava:18.0' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.9.5' } https://bitbucket.org/hvisser/android-apt
  5. Dependencies apply plugin: 'com.neenbedankt.android-apt' dependencies { apt 'com.google.dagger:dagger-compiler:2.0' compile 'com.google.dagger:dagger:2.0'

    compile 'com.google.guava:guava:18.0' paidCompile project(':lvl') testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.9.5' }
  6. Proguard buildTypes { release { minifyEnabled true shrinkResources true proguardFiles

    getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg' } } proguard.cfg #Dagger2 -dontwarn dagger.shaded.auto.common.** -dontwarn dagger.internal.codegen.** -keepattributes Signature
  7. Archive Release ext.VERSION_NAME=1.0.0 ... task archiveRelease(type: Copy) { from 'build/outputs/apk',

    'build/outputs/' into "releases/${VERSION_CODE}" include('android-release.apk', 'mapping/**') rename ('android-release.apk', "${APP_NAME}_${BUILD_DATE}_${VERSION_NAME}_${VERSION_CODE}.apk") }
  8. Scaling up • re-use • testing • quality assurance •

    continuous integration • push-button release
  9. Re-use - Core project dependencies { compile project(':core') } app/build.gradle

    core/ gradle init --type java-library include ':core' settings.gradle
  10. Re-use - Annotation processing apply plugin: 'net.ltgt.apt' dependencies { apt

    'com.google.auto.value:auto-value:1.1' compile 'com.squareup.okhttp:okhttp:2.5.0' ... } core/build.gradle https://github.com/tbroyer/gradle-apt-plugin
  11. gradle init --type groovy-library Re-use - Script client scripts/ dependencies

    { compile project(':core') } scripts/build.gradle include ':scripts' settings.gradle
  12. Re-use - Script client def operations = createUserOperations() (1..100).each {

    try { def profile = operations.readProfile(it) println profile } catch (e) { println e.message } } scripts/src/main/groovy/UserExplorer.groovy
  13. Testing - Integration Tests in Core sourceSets { integrationTest {

    java { srcDir 'src/integrationtest/java' } resources { srcDir 'src/integrationtest/resources' } compileClasspath += sourceSets.main.runtimeClasspath } } task integrationTest(type: Test) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath += sourceSets.integrationTest.runtimeClasspath }
  14. https://github.com/ben-manes/gradle-versions-plugin Keeping Up To Date apply plugin: 'com.github.ben-manes.versions' $ gw

    dependencyUpdates The following dependencies have later milestone versions: - com.fasterxml.jackson.core:jackson-annotations [2.1.2 -> 2.6.0- rc1] - com.fasterxml.jackson.core:jackson-core [2.1.1 -> 2.6.0-rc1] - com.fasterxml.jackson.core:jackson-databind [2.1.2 -> 2.6.0-rc1] - com.github.kevinsawicki:http-request [4.0 -> 9.9] - joda-time:joda-time [2.3 -> 2.8] - org.mockito:mockito-core [1.9.0 -> 2.0.8-beta] Generated report file build/dependencyUpdates/report.txt
  15. Code Quality Tools apply plugin: 'checkstyle' task checkstyleMain( type: Checkstyle)

    { description 'Checks whether code complies with coding rules.' ignoreFailures !project.failFastOnError ... doLast { if (project.hasProperty('checkstyle.html')) { ant.xslt(in: reports.xml.destination, style: file("$rulesDir/checkstyle/noframes-sorted. xsl"), out: new File(reports.xml.destination.parent, name - 'checkstyle' + '.html')) } } }
  16. Code Quality Tools apply plugin: 'checkstyle' task checkstyleMain(type: Checkstyle) {

    description 'Checks whether code complies with coding rules.' ignoreFailures !project. failFastOnError ... doLast { if (project.hasProperty(' checkstyle.html')) { ant.xslt(in: reports.xml.destination, style: file("$rulesDir/checkstyle/noframes-sorted. xsl"), out: new File(reports.xml.destination.parent, name - 'checkstyle' + '.html')) } } } $ gw checkstyleMain -PfailFastOnError -Pcheckstyle.html
  17. Code Quality Tools apply plugin: 'checkstyle' task checkstyleMain(type: Checkstyle) {

    description 'Checks whether code complies with coding rules.' ignoreFailures !project.failFastOnError ... doLast { if (project.hasProperty('checkstyle.html')) { ant.xslt(in: reports.xml.destination, style: file("$rulesDir/checkstyle/noframes-sorted. xsl"), out: new File(reports.xml.destination.parent, name - 'checkstyle' + '.html')) } } }
  18. Updating the Android SDK apply plugin: 'android-sdk-manager' $ gw assembleDebug

    Android SDK not found. Downloading... > Configuring > 1/5 projects > :android SDK extracted at '/Users/leck/.android-sdk'. Writing to local. properties. Build tools 21.1.2 missing. Downloading... > Configuring > 2/5 projects Platform tools missing. Downloading... Compilation API android-21 missing. Downloading… ... https://github.com/JakeWharton/sdk-manager-plugin
  19. Release from CI - Versioning def versionFile = file('version.properties') Version

    version = Version.read(versionFile) task incrementVersion << { Version.increment(versionFile) }
  20. Release from CI - Versioning def versionFile = file('version.properties') Version

    version = Version.read(versionFile) task incrementVersion << { Version.increment(versionFile) } buildSrc └── src └── main └── groovy └── Version.groovy
  21. Release from CI - git buildscript { dependencies { classpath

    'org.ajoberstar:grgit:1.1.0' } } ext { git = org.ajoberstar.grgit.Grgit.open file('.') } git.with { add 'version.properties' commit(message: "Bumping version to $VERSION_CODE") push() } https://github.com/ajoberstar/grgit
  22. Publishing - Github def release = repository.createRelease(releaseTag) .name(releaseName) .body(releaseBody) .commitish(commitish)

    .draft(draft) .create() assets.each { asset -> release.uploadAsset(asset, 'application/octet-stream') } buildSrc/src/main/groovy/ReleaseToGithub.groovy extends org.gradle.api.DefaultTask https://github.com/kohsuke/github-api
  23. Publishing - Github applicationVariants.all { variant -> project.task("releaseGithub$variant.name", type: ReleaseToGithub)

    { releaseTag = "${new Date().format('YY.MM.dd')}" releaseName = buildReleaseName(variant) releaseBody = buildReleaseBody(variant) assets = files(apkFile, mappingFile) commitish = project.latestCommitHash ... } } app/build.gradle
  24. Publishing - Google Play app/build.gradle apply plugin: 'com.github.triplet.play' adds Tasks:

    bootstrapReleasePlayResources publishApkRelease publishListingRelease publishRelease https://github.com/Triple-T/gradle-play-publisher
  25. Shared dependencies dependencies.gradle ext { guava_lib = 'com.google.guava:guava:18.0' ... testing_libs

    = [junit_lib, assertJ_lib] } build.gradle apply from: 'dependencies.gradle' dependencies { compile guava_lib ... testCompile testing_libs }
  26. Shared buildConfig - before android { ... buildTypes { debug

    { buildConfigField 'String', 'TAG', '"DEBUG_TAG"' ... } release { buildConfigField 'String', 'TAG', '"RELEASE_TAG"' ... } } } app/build.gradle
  27. Shared buildConfig - after android { ... buildTypes { debug

    { tag 'DEBUG_TAG' ... } release { tag 'RELEASE_TAG' ... } } } app/build.gradle
  28. Shared buildConfig - how ext.tag = { tag -> buildConfigField

    'String', 'TAG', '"'+ tag +'"' } android { buildTypes.all { apply from: 'buildconfig.gradle', to: delegate } ... } buildconfig.gradle build.gradle
  29. Sync images task syncPhotos(type: com.novoda.gradle.command.Files) { deviceId { def moto

    = android.command.devices().find { it.brand() == 'motorola' } moto.id } script { def backupDir = mkdir 'motoPhoto' list('/sdcard/DCIM/Camera/').findAll { image -> !new File(backupDir.path, image.name).exists() }.each { image -> pull image.path + image.name, backupDir.path } } }
  30. Library Shrinking dependencies { classpath 'net.sf.proguard:proguard-gradle:4.11' } ... task shrinkGuava(type:

    proguard.gradle.ProGuardTask) { injars configurations.injar.files libraryjars configurations.libjar.files outjars file("build/libs/guava_${guavaVersion}_base.jar") configuration 'guava.proguard' } guava.proguard: -dontoptimize -dontobfuscate -keep public class com.google.common.base.** { public *; }
  31. War story - Facebook keytool -exportcert -alias <key> -keystore <keystore>

    \ | openssl sha1 -binary \ | openssl base64 $ ga0RGNYHvNM5d0SLGQfpQWAPGJ8= Facebook docs on generating key hashes:
  32. War story - Facebook def message = 'keytool error: java.io.IOException:

    Keystore was tampered with, or password was incorrect\\n' def digest = MessageDigest.getInstance('SHA-1') def digestBytes = digest.digest(message.bytes) println digestBytes.encodeBase64() $ ga0RGNYHvNM5d0SLGQfpQWAPGJ8=
  33. War story - Facebook def signingMeta = com.android...tasks.SigningReportTask.metaClass MetaMethod metaFinger

    = signingMeta .getStaticMetaMethod('getFingerprint', java.security.cert.Certificate, String) signingMeta.static.getFingerprint = { Certificate cert, String alg -> def base64 = MessageDigest.getInstance(alg) .digest(cert.encoded).encodeBase64() metaFinger.invoke(delegate, cert, hashAlgorithm) + "\n$hashAlgorithm (base64): $base64" } $ gw app:signingReport
  34. Groovy on Android buildscript { repositories { jcenter() } dependencies

    { classpath 'com.android.tools.build:gradle:1.1.0' classpath 'org.codehaus.groovy:gradle-groovy-android-plugin:0.3.6' } } apply plugin: 'groovyx.grooid.groovy-android' dependencies { compile 'org.codehaus.groovy:groovy:2.4.3:grooid' }
  35. TDTD - Tester-Driven Talk Development Thanks to the folks from

    Android Stammtisch Berlin for serving as Testers for a lot of the presented material. Upcoming book: Growing Subject-Oriented Talks, Guided by Testers
  36. Resources (in order of appearance) Gradle https://github.com/gradle/gradle Android plugin for

    Gradle https://developer.android.com/tools/building/plugin-for-gradle.html gdub Tool https://github.com/dougborg/gdub Command Plugin https://github.com/novoda/gradle-android-command-plugin Gradle Plugin Portal http://plugins.gradle.org
  37. Resources (in order of appearance) apt Plugin https://bitbucket.org/hvisser/android-apt Proguard Annotation

    Squadleader https://bitbucket.org/littlerobots/squadleader Proguard Snippets https://github.com/krschultz/android-proguard-snippets Gradle Udacity Course https://www.udacity.com/course/gradle-for-android-and-java--ud867
  38. Resources (in order of appearance) Versions Plugin https://github.com/ben-manes/gradle-versions-plugin Code Analysis

    Plugin https://github.com/dlabs/soter Groovy library for git https://github.com/ajoberstar/grgit SDK Manager Plugin https://github.com/JakeWharton/sdk-manager-plugin
  39. Resources (in order of appearance) Shrink Guava Script Snippet https://gist.github.com/devisnik/e54ea0a629adc82bcfa0

    DexCount https://github.com/KeepSafe/dexcount-gradle-plugin Sqlite Analyzer Plugin https://github.com/novoda/sqlite-analyzer Groovy Android Plugin https://github.com/groovy/groovy-android-gradle-plugin