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

Droidcon Kraków 2017 - Gradle Android Recipes E...

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Droidcon Kraków 2017 - Gradle Android Recipes Explained

This talk will cover some of the more and less popular Gradle snippets and recipes that Android developers often include in their projects. We will understand exactly how and why they work, so that we can confidently modify them or expand to fit our needs.

Avatar for Łukasz Wasylkowski

Łukasz Wasylkowski

December 01, 2017

More Decks by Łukasz Wasylkowski

Other Decks in Programming

Transcript

  1. Gradle Configuration scripts Gradle core Gradle plugins Groovy Kotlin Project

    Task Dependency Extension java android-application retrolambda ❤ ❤ ❤ ❤ ❤
  2. Groovy - parentheses def passToClosure(String param, Closure closure) { closure(param)

    } passToClosure("param", { println it }) passToClosure("param") { println it } Groovy
  3. Groovy - parentheses def passToClosure(String param, Closure closure) { closure(param)

    } passToClosure("param", { println it }) passToClosure("param") { println it } passToClosure "param", { println it } Groovy
  4. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } Groovy - parentheses
  5. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } buildscript Groovy - parentheses
  6. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven { url 'https:!"maven.fabric.io/public' } google() } … } Groovy - parentheses
  7. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } ext.kotlinVersion = "1.1.2-4" Groovy - parentheses
  8. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } repositories Groovy - parentheses
  9. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } { jcenter() maven { url 'https:!"maven.fabric.io/public' } google() } Groovy - parentheses
  10. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } jcenter() Groovy - parentheses
  11. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } maven Groovy - parentheses
  12. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } { url 'https:!"maven.fabric.io/public' } Groovy - parentheses
  13. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } url ‘https:!"maven.fabric.io/public' Groovy - parentheses
  14. buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven {

    url 'https:!"maven.fabric.io/public' } google() } … } google() Groovy - parentheses
  15. Groovy - map parameters def methodWithNamedParams(Map<String, Object> params) { println

    params["name"] println "Age: ${params["age"]}" } methodWithNamedParams name: "John", age: 25
  16. Groovy - map parameters apply plugin: “com.android.application” Documentation: • from:

    A script to apply. • plugin: The id or implementation class of the plugin to apply. • to: The target delegate object or objects. apply(plugin: “com.android.application”) apply(Map<String, ?> options)
  17. def helloWorld = { println "Hello World" } def helloWorld

    = { suffix !# println "Hello World$suffix" } Groovy - closures helloWorld() helloWorld("!")
  18. def helloWorld = { println "Hello World" } def helloWorld

    = { suffix !# println "Hello World$suffix" } Groovy - closures def helloWorld = { println "Hello World$it” } helloWorld() helloWorld("!")
  19. println square(5) Groovy - closures it * it def transform(value,

    transformation) { transformation(value) } def square = { }
  20. println square(5) Groovy - closures it * it def transform(value,

    transformation) { transformation(value) } println transform(6, square) def square = { }
  21. println square(5) Groovy - closures it * it def transform(value,

    transformation) { transformation(value) } println transform(6, square) println transform(6, { it * it }) def square = { }
  22. println square(5) Groovy - closures it * it def transform(value,

    transformation) { transformation(value) } println transform(6, square) println transform(6, { it * it }) def square = { } println transform(6) { it * it }
  23. Groovy - delegates class WriterOne { def printText(str) { println

    "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } } def printClosure = { printText “Hello" }
  24. Groovy - delegates class WriterOne { def printText(str) { println

    "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } } def printClosure = { printText “Hello" } printClosure.delegate = new WriterOne() printClosure() !" “Printed in One: Hello”
  25. Groovy - delegates class WriterOne { def printText(str) { println

    "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } } def printClosure = { printText “Hello" } printClosure.delegate = new WriterOne() printClosure() !" “Printed in One: Hello” printClosure.delegate = new WriterTwo() printClosure() !" "Printed in Two: Hello”
  26. Groovy - multiple dispatch class Foo { def methodMissing(String name,

    def args) { return "$name($args) called" } }
  27. Groovy - multiple dispatch class Foo { def methodMissing(String name,

    def args) { return "$name($args) called" } } new Foo().someUnknownMethod(42)
  28. Groovy - multiple dispatch Result: someUnknownMethod([42]) called class Foo {

    def methodMissing(String name, def args) { return "$name($args) called" } } new Foo().someUnknownMethod(42)
  29. (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │ └──

    wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Initialization Configuration Execution Gradle - build phases
  30. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Initialization Configuration Execution Gradle - build phases
  31. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Initialization Configuration Execution Gradle - build phases
  32. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Initialization Configuration Execution Gradle - build phases init.gradle
  33. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Settings Initialization Configuration Execution Gradle - build phases init.gradle
  34. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Settings Initialization Configuration Execution Gradle - build phases init.gradle
  35. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) Settings Initialization Configuration Execution Gradle - build phases init.gradle
  36. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) Settings Initialization Configuration Execution Gradle - build phases init.gradle
  37. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) Settings Initialization Configuration Execution Gradle - build phases init.gradle
  38. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) rootProject Settings Initialization Configuration Execution Gradle - build phases init.gradle
  39. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) rootProject Settings Initialization Configuration Execution Gradle - build phases init.gradle
  40. Gradle (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │

    └── wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) rootProject Settings Initialization Configuration Execution Gradle - build phases init.gradle
  41. buildscript { repositories { jcenter() maven { url ‘https:!"maven.google.com' }

    } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' } } Gradle - configuring projects (build.gradle)
  42. buildscript buildscript { repositories { jcenter() maven { url ‘https:!"maven.google.com'

    } } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' } } Gradle - configuring projects (build.gradle)
  43. buildscript buildscript { repositories { jcenter() maven { url ‘https:!"maven.google.com'

    } } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' } } Project buildscript(@DelegatesTo(ScriptHandler.class) Closure closure) Gradle - configuring projects (build.gradle)
  44. repositories dependencies buildscript { repositories { jcenter() maven { url

    ‘https:!"maven.google.com' } } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' } } ScriptHandler repositories(@DelegatesTo(RepositoryHandler.class) Closure closure) dependencies(@DelegatesTo(DependencyHandler.class) Closure closure) Gradle - configuring projects (build.gradle)
  45. jcenter() maven buildscript { repositories { jcenter() maven { url

    ‘https:!"maven.google.com' } } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' } } jcenter() maven(@DelegatesTo(MavenArtifactRepository.class) Closure closure) RepositoryHandler Gradle - configuring projects (build.gradle)
  46. Project Extensions apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig

    { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } } Gradle - configuring extensions
  47. Project Extensions AppExtension apply plugin: 'com.android.application' apply plugin: 'com.android.application' android

    { compileSdkVersion 26 defaultConfig { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } } Gradle - configuring extensions
  48. Project Extensions AppExtension compileSdkVersion defaultConfig { } buildTypes { }

    apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } } compileSdkVersion defaultConfig buildTypes . . . Gradle - configuring extensions
  49. T T T T T T NamedDomainObjectCollection<T> Live notifications and

    filtering name4: name2: name5: name3: name1: name6: Gradle - Containers
  50. NamedDomainObjectSet<T> Live notifications and filtering T name6: T name5: T

    name4: T name3: T name2: T name1: Gradle - Containers
  51. NamedDomainObjectContainer<T> Live notifications and filtering T name6: T name5: T

    name4: T name3: T name2: T name1: create() maybeCreate() configure() Gradle - Containers
  52. class Person { def name def role @Override String toString()

    { return "$name !$ $role" } def role(newRole) { this.role = newRole } } Gradle - Containers
  53. println "Creating the registry” NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) println "Adding

    people” peopleRegistry.add(new Person(name: "Amy", role: “Tester")) peopleRegistry.add(new Person(name: "John", role: "Engineer")) println "Registering the listener” peopleRegistry.each { person !# println person } ▶ ./gradlew Creating the registry Registering the listener Adding people
  54. println "Creating the registry” NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) println "Adding

    people” peopleRegistry.add(new Person(name: "Amy", role: “Tester")) peopleRegistry.add(new Person(name: "John", role: "Engineer")) println "Registering the listener” peopleRegistry.all { person !# println person } ▶ ./gradlew Creating the registry Registering the listener Adding people Amy !$ Tester John !$ Engineer
  55. android { buildTypes { release { … } } }

    public void buildTypes(Action<NamedDomainObjectContainer<BuildType!& action) { this.checkWritability(); action.execute(this.buildTypes); } AppExtension: Android - build types container
  56. Android - build types container Project Extensions apply plugin: 'com.android.application'

    android { compileSdkVersion 26 defaultConfig { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } }
  57. Android - build types container Project Extensions android apply plugin:

    'com.android.application' apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } }
  58. Android - build types container Project Extensions android compileSdkVersion defaultConfig

    { } buildTypes { } apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } } compileSdkVersion defaultConfig buildTypes . .
  59. Android - build types container Project Extensions android buildTypes debug

    compileSdkVersion defaultConfig { } buildTypes { } apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } } compileSdkVersion defaultConfig buildTypes . .
  60. Android - build types container Project Extensions android buildTypes debug

    staging staging { } apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.tooploox.gradle" minSdkVersion 21 versionName "1.0" } buildTypes { debug { debuggable true } staging { debuggable false } } } compileSdkVersion defaultConfig buildTypes . .
  61. android { applicationVariants.all { variant !# variant.outputs.each { it.outputFile =

    new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } } APK naming
  62. public DomainObjectSet<ApplicationVariant> AppExtension#getApplicationVariants() android { applicationVariants.all { variant !# variant.outputs.each

    { it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } } applicationVariants.all APK naming
  63. android { applicationVariants.all { variant !# variant.outputs.each { it.outputFile =

    new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } } variant.outputs.each { public List<BaseVariantOutput> ApplicationVariant#getOutputs() APK naming
  64. android { applicationVariants.all { variant !# variant.outputs.each { it.outputFile =

    new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } } it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) public File BaseVariantOutput#getOutputFile() public File BaseVariantOutput#setOutputFile(File file) APK naming
  65. def androidProjects = [project("app"), project("data"), project("mvp")] androidProjects.each { project !#

    androidBuildTypes.each { project.android.buildTypes.maybeCreate it } androidFlavors.each { project.android.productFlavors.maybeCreate it } } def androidBuildTypes = ["alpha", "debug", "release"] def androidFlavors = ["staging", “production", “mock”] Common flavors and build types
  66. android { buildTypes { release { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "false"

    buildConfigField "boolean", "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "false" } debug { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "true" buildConfigField "boolean", "REPORT_CRASHES", "false" buildConfigField "boolean", "DEBUG_IMAGES", "true" } !!' } } BuildConfig fields
  67. android { buildTypes { def BOOLEAN = "boolean" def TRUE

    = "true" def FALSE = "false" def LOG_HTTP_REQUESTS = "LOG_HTTP_REQUESTS" def REPORT_CRASHES = "REPORT_CRASHES" def DEBUG_IMAGES = "DEBUG_IMAGES" release { buildConfigField BOOLEAN, LOG_HTTP_REQUESTS, FALSE buildConfigField BOOLEAN, REPORT_CRASHES, TRUE buildConfigField BOOLEAN, DEBUG_IMAGES, FALSE } debug { buildConfigField BOOLEAN, LOG_HTTP_REQUESTS, TRUE buildConfigField BOOLEAN, REPORT_CRASHES, FALSE buildConfigField BOOLEAN, DEBUG_IMAGES, TRUE } } }
  68. buildTypes { release { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "false" buildConfigField "boolean",

    "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" } } BuildConfig fields
  69. void buildTypes(Action<NamedDomainObjectContainer<BuildType!& action) buildTypes { release { buildConfigField "boolean", "LOG_HTTP_REQUESTS",

    "false" buildConfigField "boolean", "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" } } buildTypes { } BuildConfig fields
  70. buildTypes { release { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "false" buildConfigField "boolean",

    "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" } } BuildType(name: release) { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "false" buildConfigField "boolean", "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" } BuildConfig fields
  71. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" } BuildConfig fields
  72. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" } configureBuildType(android.buildTypes.release, false) BuildConfig fields
  73. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" } android { buildTypes { release { configureBuildType(???, false) } debug { configureBuildType(???, true) } } } BuildConfig fields
  74. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" } android { buildTypes { release { configureBuildType(???, false) } debug { configureBuildType(???, true) } } } android { buildTypes { release { configureBuildType(delegate, false) } debug { configureBuildType(delegate, true) } } } BuildConfig fields
  75. android { buildTypes { release { configureBuildType(delegate, false) } }

    } android { buildTypes { release { configureBuildType(delegate, false) } debug { configureBuildType(delegate, true) } alpha { configureBuildType(delegate, false) } mock { configureBuildType(delegate, true) }
  76. androidTestCompile (‘com.android.support.test.espresso:espresso-contrib:2.2') { exclude group: 'com.android.support', module: 'appcompat-v7' exclude group:

    'com.android.support', module: 'support-v4' exclude module: 'recyclerview-v7' } compile('com.crashlytics.sdk.android:crashlytics:2.6.7@aar') { transitive = true } Dependency resolution
  77. ▶ ./gradlew app:dependencies !)configuration compile dependencies { compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' }

    compile - Dependencies for source set 'main' +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*) | \!!* com.android.support:support-annotations:25.1.1 !# 25.3.1 Dependency resolution
  78. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'

    +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0
  79. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 RxJava 2.0.0

    compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*)
  80. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 RxJava 2.0.0

    +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*) 2.0.0 !# 2.0.2 (*) RxJava 2.0.2 RxJava 2.0.0
  81. ▶ ./gradlew app:dependencies !)configuration compile dependencies { compile(‘com.jakewharton.rxbinding2:rxbinding:2.0.0’) { transitive

    = false } } compile - Dependencies for source set 'main' +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 Dependency resolution
  82. ▶ gw app:dependencies !)configuration androidTestCompile androidTestCompile - Classpath for compiling

    the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support:support-v4:22.2.0 | \!!* com.android.support:support-annotations:22.2.0 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*) androidTestCompile - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2' }
  83. ▶ gw app:dependencies !)configuration androidTestCompile androidTestCompile - Classpath for compiling

    the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support:support-v4:22.2.0 | \!!* com.android.support:support-annotations:22.2.0 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*) androidTestCompile - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2' compile ‘com.android.support:recyclerview-v7:25.3.1' }
  84. dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2' compile ‘com.android.support:recyclerview-v7:25.3.1' } Conflict with dependency

    'com.android.support:recyclerview-v7' in project ‘:app'. Resolved versions for app (25.3.1) and test app (22.2.0) differ. See http:!"g.co/androidstudio/app-test-app-conflict for details. Conflicting dependencies
  85. androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support' } androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group:

    'com.android.support', module: 'appcompat-v7' exclude group: 'com.android.support', module: 'support-v4' exclude module: 'recyclerview-v7' } Conflicting dependencies - solving
  86. androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support' } androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group:

    'com.android.support', module: 'appcompat-v7' exclude group: 'com.android.support', module: 'support-v4' exclude module: 'recyclerview-v7' } androidTestCompile - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*) Conflicting dependencies - solving
  87. androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support' } androidTestCompile - Classpath for

    compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*) Conflicting dependencies - solving
  88. configurations.all { resolutionStrategy { eachDependency { DependencyResolveDetails details !# if

    (details.requested.group !, 'com.android.support') { details.useVersion “27.0.1” } } } } Conflicting dependencies - solving