Level Up Your Android Build -Droidcon Berlin 2015

47
Level Up Your Android Build - droidcon Berlin 2015 LEVEL UP YOUR ANDROID BUILD Volker Leck Friedger Müffke Droidcon Berlin 2015

Transcript of Level Up Your Android Build -Droidcon Berlin 2015

Page 1: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

LEVEL UP YOUR ANDROID BUILDVolker Leck

Friedger Müffke

Droidcon Berlin 2015

Page 2: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

image

Page 3: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

image

Phoogle Gotos

Page 4: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

app/build.gradleapply plugin: 'com.android.application'

android { compileSdkVersion 22 buildToolsVersion "22.2.0"

defaultConfig {...

} buildTypes { release {

... } }}

dependencies {...

}

Page 5: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

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

Page 6: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Gradle wrapper$ ./gradlew tasks

Page 7: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Gradle wrapper$ gw clean ass

Page 8: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Using a Gradle pluginbuild.gradle (root folder)

buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.2.3'

classpath 'com.novoda:gradle-android-command-plugin:1.4.0' }}

apply plugin: 'com.novoda.android-command'

$ gw runDebug

Page 9: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Dependencies

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' }

Page 10: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Dependencies

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' }

Page 11: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Flavors (free - paid)

Page 12: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

ProguardbuildTypes { 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

Page 13: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

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")}

Page 14: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

image

Page 15: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

image

Page 16: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Scaling up● re-use● testing● quality assurance● continuous integration

Page 17: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Re-use - Core project

gradle init --type java-library

dependencies { compile project(':model')}

Page 18: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Re-use - Core project

gradle init --type java-library

dependencies { compile project(':model')}

gradle init --type groovy-library

println new URL('http://www.google.com').text

Page 19: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Separation - Integration Tests for JavasourceSets {

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

}

Page 20: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Keep Up To Date

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

Page 21: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Code Quality Toolsapply 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')) } }}

Page 22: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Code Quality Toolsapply 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

Page 23: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Code Quality Toolsapply 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')) } }}

Page 24: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Continuous IntegrationSystem.getenv("...")

Jenkins

BUILD_NUMBER

Travis

TRAVIS_BUILD_NUMBER

Page 25: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Connected Tests on CItask filterTestDevices << { com.android.builder.testing.ConnectedDeviceProvider.metaClass

.getDevices = { localDevices .findAll { it.iDevice.online } .findAll { !it.getProperty('ro.build.characteristics')

?.toLowerCase()?.contains('tablet') } .unique { it.apiLevel } }}

Page 26: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Connected Tests on CI

afterEvaluate { project.tasks.findAll {

it.name.startsWith('connectedAndroidTest') }*.dependsOn filterTestDevices

}

Page 27: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Releasing from CI

● update Version

● commit changes

● upload to Stores

Page 28: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Release from CI - Versioning

def versionFile = file('version.properties')

Version version = Version.read(versionFile)

task incrementVersion << { Version.increment(versionFile)}

Page 29: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Release from CI - Versioning

def versionFile = file('version.properties')

Version version = Version.read(versionFile)

task incrementVersion << { description = 'Increases the version' Version.increment(versionFile)}

buildSrc/└── src └── main └── groovy └── Version.groovy

Page 30: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Release from CI - gitbuildscript { dependencies { classpath 'org.ajoberstar:grgit:1.1.0' }}ext { git = org.ajoberstar.grgit.Grgit.open(

rootProject.file('.'))}def sha = git.head().abbreviatedIdgit.add 'version.properties' git.commit(message: "Bumping version to $VERSION_CODE")git.push()

Page 31: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Publishing - Google Playplay-publisher-plugin by triple-t● bootstrapReleasePlayResources - Fetch all existing data from the Play

Store to bootstrap the required files and folders.● publishApkRelease - Uploads the APK and the summary of recent

changes.● publishListingRelease - Uploads the descriptions and images for the Play

Store listing.● publishRelease - Uploads everything.

Page 32: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Publishing - Google Playplay-publisher-plugin by triple-t● bootstrapReleasePlayResources - Fetch all existing data from the Play

Store to bootstrap the required files and folders.● publishApkRelease - Uploads the APK and the summary of recent

changes.● publishListingRelease - Uploads the descriptions and images for the Play

Store listing.● publishRelease - Uploads everything.play {

serviceAccountEmail = 'your-service-account-email' pk12File = file('key.p12')}

Page 33: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Publishing - Other App Stores

task createAppDFs {

android.applicationVariants.all { variant ->

if (variant.buildType.name == "release")

createAppDF(variant.name)

}

}

Page 34: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Advanced - Complex Build Scriptsshare common configuration data via root subfolder

File shared(String relativeFilePath) { file("shared/$relativeFilePath")

}

share common dependencies via root build.gradleext {

guavaDependency = 'com.google.guava:guava:18.0'...

}

use it in sub-projects via:dependencies {

compile guavaDependency...

}

Page 35: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Advanced - Shrinkdependencies { 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 *; }

Page 36: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Advanced - Sync imagestask syncPhotos(type: com.novoda.gradle.command.Files) { deviceId { def moto = android.command.devices().find { it.brand() == 'motorola' } if (!moto) { throw new GroovyRuntimeException('No Motorola device') } 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 } }}

Page 37: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Advanced - Sqlite AccesssqliteAccess { migrationsDir 'src/main/assets/migrations' packageName 'com.novoda.sqliteprovider.demo.simple' generator { database, basedir -> def access = access(database) // …, adjust access model as needed generateClass(file('code.template'), access, "DB",

packageName, basedir) }}

Page 38: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Advanced - Sqlite Accesscode.template

package $packageName;

public final class $className {public static final class Tables {

<% access.tables.each { dataSet -> %> public static final String $dataSet.name = "$dataSet.sqlName";<% } %>

}...

}

Page 39: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Advanced - Groovy on Androidbuildscript { 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'}

Page 40: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Resources (in order of appearance)Android plugin for Gradle

https://developer.android.com/tools/building/plugin-for-gradle.html

gdub Tool

https://github.com/dougborg/gdub

apt Plugin

https://bitbucket.org/hvisser/android-apt

Command Plugin

https://github.com/novoda/gradle-android-command-plugin

Gradle Plugin Portal

http://plugins.gradle.org

Page 41: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Resources (in order of appearance)Proguard Annotation Squadleader

https://bitbucket.org/littlerobots/squadleader

Proguard Snippets

https://github.com/krschultz/android-proguard-snippets

Gradle Udacity Course

https://www.udacity.com/wiki/ud867

Groovy library for git

https://github.com/ajoberstar/grgit

Page 42: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Resources (in order of appearance)Soter Plugin

https://github.com/dlabs/soter

Shrink Guava Script Snippet

https://gist.github.com/devisnik/e54ea0a629adc82bcfa0

Groovy Android Plugin

https://github.com/groovy/groovy-android-gradle-plugin

Sqlite Analyzer Plugin

https://github.com/novoda/sqlite-analyzer

Page 43: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

TDTD - Tester-Driven Talk DevelopmentThanks 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

Page 44: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Friedger Müffke Volker Leck

fmdroid devisnik

+FriedgerMüffke +VolkerLeck

friedger devisnik

Page 45: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Build tooling

Page 46: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Problems● no re-use outside android app● limited and manual code quality assurance (via test feedback)● limited automation

○ manual versioning○ manual deployment○ no continuous integration○ tedious distribution (beta channels, bug tracking, ..)○ dependent on Android SDK installation

Page 47: Level Up Your Android Build -Droidcon Berlin 2015

Level Up Your Android Build - droidcon Berlin 2015

Separation - Test Executiontest { useJUnit { excludeCategories 'com.sample.api.LiveTest' }}task endpointTest(type: Test, dependsOn: testClasses) { useJUnit { includeCategories 'com.sample.api.LiveTest' }}

annotate tests with: @Category(LiveTest.class)